mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-11-10 19:56:47 +03:00
Merge branch 'master' into license
This commit is contained in:
commit
5d9c975c46
136
.travis.yml
136
.travis.yml
|
@ -6,104 +6,55 @@ notifications:
|
||||||
# Run slow PyPy* first, to give them a headstart and reduce waiting time.
|
# Run slow PyPy* first, to give them a headstart and reduce waiting time.
|
||||||
# Run latest 3.x and 2.x next, to get quick compatibility results.
|
# Run latest 3.x and 2.x next, to get quick compatibility results.
|
||||||
# Then run the remainder.
|
# Then run the remainder.
|
||||||
python:
|
|
||||||
- "pypy"
|
matrix:
|
||||||
- "pypy3"
|
fast_finish: true
|
||||||
- 3.5
|
allow_failures:
|
||||||
- 2.7
|
- python: nightly
|
||||||
- 2.6
|
include:
|
||||||
- "2.7_with_system_site_packages" # For PyQt4
|
- python: "pypy"
|
||||||
- 3.2
|
- python: "pypy3"
|
||||||
- 3.3
|
- python: '3.6'
|
||||||
- 3.4
|
- python: '2.7'
|
||||||
- nightly
|
- env: DOCKER="alpine"
|
||||||
|
- env: DOCKER="ubuntu-trusty-x86"
|
||||||
|
- env: DOCKER="ubuntu-xenial-amd64"
|
||||||
|
- env: DOCKER="ubuntu-precise-amd64"
|
||||||
|
- python: "2.7_with_system_site_packages" # For PyQt4
|
||||||
|
- python: '3.5'
|
||||||
|
- python: '3.4'
|
||||||
|
- python: '3.3'
|
||||||
|
- python: 'nightly'
|
||||||
|
|
||||||
|
dist: trusty
|
||||||
|
|
||||||
|
sudo: required
|
||||||
|
|
||||||
|
services:
|
||||||
|
- docker
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- "travis_retry pip install pycparser!=2.14" # TEMPORARY, WORKAROUND FOR https://github.com/eliben/pycparser/issues/147
|
- if [ "$DOCKER" == "" ]; then .travis/install.sh; fi
|
||||||
- "travis_retry sudo apt-get update"
|
|
||||||
- "travis_retry sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-qt4 ghostscript libffi-dev libjpeg-turbo-progs cmake imagemagick"
|
|
||||||
- "travis_retry pip install cffi"
|
|
||||||
- "travis_retry pip install nose"
|
|
||||||
- "travis_retry pip install check-manifest"
|
|
||||||
# Pyroma tests sometimes hang on PyPy and Python 2.6; skip for those
|
|
||||||
- if [ $TRAVIS_PYTHON_VERSION != "pypy" && $TRAVIS_PYTHON_VERSION != "2.6" ]; then travis_retry pip install pyroma; fi
|
|
||||||
|
|
||||||
- if [ "$TRAVIS_PYTHON_VERSION" == "2.6" ]; then travis_retry pip install unittest2; fi
|
before_install:
|
||||||
|
- if [ "$DOCKER" ]; then docker pull pythonpillow/$DOCKER; fi
|
||||||
|
|
||||||
# Coverage 4.0 doesn't support Python 3.2
|
before_script:
|
||||||
- if [ "$TRAVIS_PYTHON_VERSION" == "3.2" ]; then travis_retry pip install coverage==3.7.1; fi
|
# Qt needs a display for some of the tests, and it's only run on the system site packages install
|
||||||
- if [ "$TRAVIS_PYTHON_VERSION" != "3.2" ]; then travis_retry pip install coverage; fi
|
- "export DISPLAY=:99.0"
|
||||||
|
- "sh -e /etc/init.d/xvfb start"
|
||||||
# docs only on python 2.7
|
|
||||||
- if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then travis_retry pip install -r requirements.txt ; fi
|
|
||||||
|
|
||||||
# clean checkout for manifest
|
|
||||||
- mkdir /tmp/check-manifest && cp -a . /tmp/check-manifest
|
|
||||||
|
|
||||||
# webp
|
|
||||||
- pushd depends && ./install_webp.sh && popd
|
|
||||||
|
|
||||||
# openjpeg
|
|
||||||
- pushd depends && ./install_openjpeg.sh && popd
|
|
||||||
|
|
||||||
# libimagequant
|
|
||||||
- pushd depends && ./install_imagequant.sh && popd
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage erase; fi
|
- |
|
||||||
- python setup.py clean
|
if [ "$DOCKER" == "" ]; then
|
||||||
- CFLAGS="-coverage" python setup.py build_ext --inplace
|
.travis/script.sh
|
||||||
|
else
|
||||||
- if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage run --append --include=PIL/* selftest.py; fi
|
docker run -v $TRAVIS_BUILD_DIR:/Pillow pythonpillow/$DOCKER
|
||||||
- if [ "$TRAVIS_PYTHON_VERSION" != "nightly" ]; then coverage run --append --include=PIL/* -m nose -vx Tests/test_*.py; fi
|
fi
|
||||||
- pushd /tmp/check-manifest && check-manifest --ignore ".coveragerc,.editorconfig,*.yml,*.yaml,tox.ini" && popd
|
|
||||||
|
|
||||||
# Docs
|
|
||||||
- if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then make install && make doccheck; fi
|
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
# gather the coverage data
|
- .travis/after_success.sh
|
||||||
- travis_retry sudo apt-get -qq install lcov
|
|
||||||
- lcov --capture --directory . -b . --output-file coverage.info
|
|
||||||
# filter to remove system headers
|
|
||||||
- lcov --remove coverage.info '/usr/*' -o coverage.filtered.info
|
|
||||||
# convert to json
|
|
||||||
- travis_retry gem install coveralls-lcov
|
|
||||||
- coveralls-lcov -v -n coverage.filtered.info > coverage.c.json
|
|
||||||
|
|
||||||
- coverage report
|
|
||||||
- travis_retry pip install coveralls-merge
|
|
||||||
- coveralls-merge coverage.c.json
|
|
||||||
|
|
||||||
- travis_retry pip install pep8 pyflakes
|
|
||||||
- pep8 --statistics --count PIL/*.py
|
|
||||||
- pep8 --statistics --count Tests/*.py
|
|
||||||
- pyflakes *.py | tee >(wc -l)
|
|
||||||
- pyflakes PIL/*.py | tee >(wc -l)
|
|
||||||
- pyflakes Tests/*.py | tee >(wc -l)
|
|
||||||
|
|
||||||
# Coverage and quality reports on just the latest diff.
|
|
||||||
# (Installation is very slow on Py3, so just do it for Py2.)
|
|
||||||
- if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then depends/diffcover-install.sh; fi
|
|
||||||
- if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then depends/diffcover-run.sh; fi
|
|
||||||
|
|
||||||
# after_all
|
|
||||||
- |
|
|
||||||
if [ "$TRAVIS_REPO_SLUG" = "python-pillow/Pillow" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then
|
|
||||||
curl -Lo travis_after_all.py https://raw.github.com/dmakhno/travis_after_all/master/travis_after_all.py
|
|
||||||
python travis_after_all.py
|
|
||||||
export $(cat .to_export_back)
|
|
||||||
if [ "$BUILD_LEADER" = "YES" ]; then
|
|
||||||
if [ "$BUILD_AGGREGATE_STATUS" = "others_succeeded" ]; then
|
|
||||||
echo "All jobs succeded! Triggering macOS build..."
|
|
||||||
# Trigger a macOS build at the pillow-wheels repo
|
|
||||||
./build_children.sh
|
|
||||||
else
|
|
||||||
echo "Some jobs failed"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
after_failure:
|
after_failure:
|
||||||
- |
|
- |
|
||||||
if [ "$TRAVIS_REPO_SLUG" = "python-pillow/Pillow" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then
|
if [ "$TRAVIS_REPO_SLUG" = "python-pillow/Pillow" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then
|
||||||
|
@ -125,11 +76,6 @@ after_script:
|
||||||
echo leader=$BUILD_LEADER status=$BUILD_AGGREGATE_STATUS
|
echo leader=$BUILD_LEADER status=$BUILD_AGGREGATE_STATUS
|
||||||
fi
|
fi
|
||||||
|
|
||||||
matrix:
|
|
||||||
fast_finish: true
|
|
||||||
allow_failures:
|
|
||||||
- python: nightly
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
# travis encrypt AUTH_TOKEN=
|
# travis encrypt AUTH_TOKEN=
|
||||||
|
|
46
.travis/after_success.sh
Executable file
46
.travis/after_success.sh
Executable file
|
@ -0,0 +1,46 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# gather the coverage data
|
||||||
|
sudo apt-get -qq install lcov
|
||||||
|
lcov --capture --directory . -b . --output-file coverage.info
|
||||||
|
# filter to remove system headers
|
||||||
|
lcov --remove coverage.info '/usr/*' -o coverage.filtered.info
|
||||||
|
# convert to json
|
||||||
|
gem install coveralls-lcov
|
||||||
|
coveralls-lcov -v -n coverage.filtered.info > coverage.c.json
|
||||||
|
|
||||||
|
coverage report
|
||||||
|
pip install coveralls-merge
|
||||||
|
coveralls-merge coverage.c.json
|
||||||
|
|
||||||
|
if [ "$DOCKER" == "" ]; then
|
||||||
|
pip install pep8 pyflakes
|
||||||
|
pep8 --statistics --count PIL/*.py
|
||||||
|
pep8 --statistics --count Tests/*.py
|
||||||
|
pyflakes *.py | tee >(wc -l)
|
||||||
|
pyflakes PIL/*.py | tee >(wc -l)
|
||||||
|
pyflakes Tests/*.py | tee >(wc -l)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ] && [ "$DOCKER" == "" ]; then
|
||||||
|
# Coverage and quality reports on just the latest diff.
|
||||||
|
# (Installation is very slow on Py3, so just do it for Py2.)
|
||||||
|
depends/diffcover-install.sh
|
||||||
|
depends/diffcover-run.sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
# after_all
|
||||||
|
|
||||||
|
if [ "$TRAVIS_REPO_SLUG" = "python-pillow/Pillow" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then
|
||||||
|
curl -Lo travis_after_all.py https://raw.github.com/dmakhno/travis_after_all/master/travis_after_all.py
|
||||||
|
python travis_after_all.py
|
||||||
|
export $(cat .to_export_back)
|
||||||
|
if [ "$BUILD_LEADER" = "YES" ]; then
|
||||||
|
if [ "$BUILD_AGGREGATE_STATUS" = "others_succeeded" ]; then
|
||||||
|
echo "All jobs succeded! Triggering macOS build..."
|
||||||
|
# Trigger a macOS build at the pillow-wheels repo
|
||||||
|
./build_children.sh
|
||||||
|
else
|
||||||
|
echo "Some jobs failed"
|
||||||
|
fi
|
||||||
|
fi
|
34
.travis/install.sh
Executable file
34
.travis/install.sh
Executable file
|
@ -0,0 +1,34 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get -qq install libfreetype6-dev liblcms2-dev\
|
||||||
|
python-qt4 ghostscript libffi-dev libjpeg-turbo-progs cmake imagemagick
|
||||||
|
pip install cffi
|
||||||
|
pip install nose
|
||||||
|
pip install check-manifest
|
||||||
|
pip install olefile
|
||||||
|
# Pyroma tests sometimes hang on PyPy; skip
|
||||||
|
if [ "$TRAVIS_PYTHON_VERSION" != "pypy" ]; then pip install pyroma; fi
|
||||||
|
|
||||||
|
pip install coverage
|
||||||
|
|
||||||
|
# docs only on python 2.7
|
||||||
|
if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then pip install -r requirements.txt ; fi
|
||||||
|
|
||||||
|
# clean checkout for manifest
|
||||||
|
mkdir /tmp/check-manifest && cp -a . /tmp/check-manifest
|
||||||
|
|
||||||
|
# webp
|
||||||
|
pushd depends && ./install_webp.sh && popd
|
||||||
|
|
||||||
|
# openjpeg
|
||||||
|
pushd depends && ./install_openjpeg.sh && popd
|
||||||
|
|
||||||
|
# libimagequant
|
||||||
|
pushd depends && ./install_imagequant.sh && popd
|
||||||
|
|
||||||
|
# extra test images
|
||||||
|
pushd depends && ./install_extra_test_images.sh && popd
|
||||||
|
|
14
.travis/script.sh
Executable file
14
.travis/script.sh
Executable file
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
coverage erase
|
||||||
|
python setup.py clean
|
||||||
|
CFLAGS="-coverage" python setup.py build_ext --inplace
|
||||||
|
|
||||||
|
coverage run --append --include=PIL/* selftest.py
|
||||||
|
coverage run --append --include=PIL/* -m nose -vx Tests/test_*.py
|
||||||
|
pushd /tmp/check-manifest && check-manifest --ignore ".coveragerc,.editorconfig,*.yml,*.yaml,tox.ini" && popd
|
||||||
|
|
||||||
|
# Docs
|
||||||
|
if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then make install && make doccheck; fi
|
598
CHANGES.rst
598
CHANGES.rst
File diff suppressed because it is too large
Load Diff
2
LICENSE
2
LICENSE
|
@ -5,7 +5,7 @@ The Python Imaging Library (PIL) is
|
||||||
|
|
||||||
Pillow is the friendly PIL fork. It is
|
Pillow is the friendly PIL fork. It is
|
||||||
|
|
||||||
Copyright © 2012-2016 by Alex Clark and contributors
|
Copyright © 2010-2017 by Alex Clark and contributors
|
||||||
|
|
||||||
Like PIL, Pillow is licensed under the open source PIL Software License:
|
Like PIL, Pillow is licensed under the open source PIL Software License:
|
||||||
|
|
||||||
|
|
7
Makefile
7
Makefile
|
@ -58,6 +58,13 @@ install:
|
||||||
python setup.py install
|
python setup.py install
|
||||||
python selftest.py --installed
|
python selftest.py --installed
|
||||||
|
|
||||||
|
debug:
|
||||||
|
# make a debug version if we don't have a -dbg python. Leaves in symbols
|
||||||
|
# for our stuff, kills optimization, and redirects to dev null so we
|
||||||
|
# see any build failures.
|
||||||
|
make clean > /dev/null
|
||||||
|
CFLAGS='-g -O0' python setup.py build_ext install > /dev/null
|
||||||
|
|
||||||
install-req:
|
install-req:
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,9 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image
|
from __future__ import print_function
|
||||||
from PIL import FontFile
|
|
||||||
|
from . import Image, FontFile
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
|
@ -119,9 +120,9 @@ class BdfFontFile(FontFile.FontFile):
|
||||||
|
|
||||||
# fontname = ";".join(font[1:])
|
# fontname = ";".join(font[1:])
|
||||||
|
|
||||||
# print "#", fontname
|
# print("#", fontname)
|
||||||
# for i in comments:
|
# for i in comments:
|
||||||
# print "#", i
|
# print("#", i)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
c = bdf_char(fp)
|
c = bdf_char(fp)
|
||||||
|
|
|
@ -24,18 +24,13 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
from PIL import Image, ImageFile, ImagePalette, _binary
|
from . import Image, ImageFile, ImagePalette
|
||||||
|
from ._binary import i8, i16le as i16, i32le as i32, \
|
||||||
|
o8, o16le as o16, o32le as o32
|
||||||
import math
|
import math
|
||||||
|
|
||||||
__version__ = "0.7"
|
__version__ = "0.7"
|
||||||
|
|
||||||
i8 = _binary.i8
|
|
||||||
i16 = _binary.i16le
|
|
||||||
i32 = _binary.i32le
|
|
||||||
o8 = _binary.o8
|
|
||||||
o16 = _binary.o16le
|
|
||||||
o32 = _binary.o32le
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Read BMP file
|
# Read BMP file
|
||||||
|
@ -73,7 +68,7 @@ class BmpImageFile(ImageFile.ImageFile):
|
||||||
read, seek = self.fp.read, self.fp.seek
|
read, seek = self.fp.read, self.fp.seek
|
||||||
if header:
|
if header:
|
||||||
seek(header)
|
seek(header)
|
||||||
file_info = dict()
|
file_info = {}
|
||||||
file_info['header_size'] = i32(read(4)) # read bmp header size @offset 14 (this is part of the header size)
|
file_info['header_size'] = i32(read(4)) # read bmp header size @offset 14 (this is part of the header size)
|
||||||
file_info['direction'] = -1
|
file_info['direction'] = -1
|
||||||
# --------------------- If requested, read header at a specific position
|
# --------------------- If requested, read header at a specific position
|
||||||
|
@ -136,12 +131,13 @@ class BmpImageFile(ImageFile.ImageFile):
|
||||||
# ----------------- Process BMP with Bitfields compression (not palette)
|
# ----------------- Process BMP with Bitfields compression (not palette)
|
||||||
if file_info['compression'] == self.BITFIELDS:
|
if file_info['compression'] == self.BITFIELDS:
|
||||||
SUPPORTED = {
|
SUPPORTED = {
|
||||||
32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0)],
|
32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0), (0xff000000, 0xff0000, 0xff00, 0x0) ],
|
||||||
24: [(0xff0000, 0xff00, 0xff)],
|
24: [(0xff0000, 0xff00, 0xff)],
|
||||||
16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]
|
16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]
|
||||||
}
|
}
|
||||||
MASK_MODES = {
|
MASK_MODES = {
|
||||||
(32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX",
|
(32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX",
|
||||||
|
(32, (0xff000000, 0xff0000, 0xff00, 0x0)): "XBGR",
|
||||||
(32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA",
|
(32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA",
|
||||||
(32, (0x0, 0x0, 0x0, 0x0)): "BGRA",
|
(32, (0x0, 0x0, 0x0, 0x0)): "BGRA",
|
||||||
(24, (0xff0000, 0xff00, 0xff)): "BGR",
|
(24, (0xff0000, 0xff00, 0xff)): "BGR",
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image, ImageFile
|
from . import Image, ImageFile
|
||||||
|
|
||||||
_handler = None
|
_handler = None
|
||||||
|
|
||||||
|
|
|
@ -16,18 +16,16 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
from PIL import Image, BmpImagePlugin, _binary
|
from . import Image, BmpImagePlugin
|
||||||
|
from ._binary import i8, i16le as i16, i32le as i32
|
||||||
|
|
||||||
__version__ = "0.1"
|
__version__ = "0.1"
|
||||||
|
|
||||||
#
|
#
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
|
|
||||||
i8 = _binary.i8
|
|
||||||
i16 = _binary.i16le
|
|
||||||
i32 = _binary.i32le
|
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix):
|
def _accept(prefix):
|
||||||
return prefix[:4] == b"\0\0\2\0"
|
return prefix[:4] == b"\0\0\2\0"
|
||||||
|
@ -58,14 +56,14 @@ class CurImageFile(BmpImagePlugin.BmpImageFile):
|
||||||
m = s
|
m = s
|
||||||
elif i8(s[0]) > i8(m[0]) and i8(s[1]) > i8(m[1]):
|
elif i8(s[0]) > i8(m[0]) and i8(s[1]) > i8(m[1]):
|
||||||
m = s
|
m = s
|
||||||
# print "width", i8(s[0])
|
# print("width", i8(s[0]))
|
||||||
# print "height", i8(s[1])
|
# print("height", i8(s[1]))
|
||||||
# print "colors", i8(s[2])
|
# print("colors", i8(s[2]))
|
||||||
# print "reserved", i8(s[3])
|
# print("reserved", i8(s[3]))
|
||||||
# print "hotspot x", i16(s[4:])
|
# print("hotspot x", i16(s[4:]))
|
||||||
# print "hotspot y", i16(s[6:])
|
# print("hotspot y", i16(s[6:]))
|
||||||
# print "bytes", i32(s[8:])
|
# print("bytes", i32(s[8:]))
|
||||||
# print "offset", i32(s[12:])
|
# print("offset", i32(s[12:]))
|
||||||
if not m:
|
if not m:
|
||||||
raise TypeError("No cursors were found")
|
raise TypeError("No cursors were found")
|
||||||
|
|
||||||
|
|
|
@ -21,15 +21,14 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image, _binary
|
from . import Image
|
||||||
from PIL.PcxImagePlugin import PcxImageFile
|
from ._binary import i32le as i32
|
||||||
|
from .PcxImagePlugin import PcxImageFile
|
||||||
|
|
||||||
__version__ = "0.2"
|
__version__ = "0.2"
|
||||||
|
|
||||||
MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then?
|
MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then?
|
||||||
|
|
||||||
i32 = _binary.i32le
|
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix):
|
def _accept(prefix):
|
||||||
return len(prefix) >= 4 and i32(prefix) == MAGIC
|
return len(prefix) >= 4 and i32(prefix) == MAGIC
|
||||||
|
|
|
@ -12,7 +12,7 @@ Full text of the CC0 license:
|
||||||
|
|
||||||
import struct
|
import struct
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from PIL import Image, ImageFile
|
from . import Image, ImageFile
|
||||||
|
|
||||||
|
|
||||||
# Magic ("DDS ")
|
# Magic ("DDS ")
|
||||||
|
|
|
@ -23,16 +23,14 @@
|
||||||
import re
|
import re
|
||||||
import io
|
import io
|
||||||
import sys
|
import sys
|
||||||
from PIL import Image, ImageFile, _binary
|
from . import Image, ImageFile
|
||||||
|
from ._binary import i32le as i32, o32le as o32
|
||||||
|
|
||||||
__version__ = "0.5"
|
__version__ = "0.5"
|
||||||
|
|
||||||
#
|
#
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
|
|
||||||
i32 = _binary.i32le
|
|
||||||
o32 = _binary.o32le
|
|
||||||
|
|
||||||
split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$")
|
split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$")
|
||||||
field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$")
|
field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$")
|
||||||
|
|
||||||
|
@ -145,7 +143,8 @@ def Ghostscript(tile, size, fp, scale=1):
|
||||||
status = gs.wait()
|
status = gs.wait()
|
||||||
if status:
|
if status:
|
||||||
raise IOError("gs failed (status %d)" % status)
|
raise IOError("gs failed (status %d)" % status)
|
||||||
im = Image.core.open_ppm(outfile)
|
im = Image.open(outfile)
|
||||||
|
im.load()
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
os.unlink(outfile)
|
os.unlink(outfile)
|
||||||
|
@ -154,7 +153,7 @@ def Ghostscript(tile, size, fp, scale=1):
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return im
|
return im.im.copy()
|
||||||
|
|
||||||
|
|
||||||
class PSFile(object):
|
class PSFile(object):
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image, ImageFile
|
from . import Image, ImageFile
|
||||||
|
|
||||||
_handler = None
|
_handler = None
|
||||||
|
|
||||||
|
|
|
@ -16,15 +16,11 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
from PIL import Image, ImageFile, ImagePalette, _binary
|
from . import Image, ImageFile, ImagePalette
|
||||||
|
from ._binary import i8, i16le as i16, i32le as i32, o8
|
||||||
|
|
||||||
__version__ = "0.2"
|
__version__ = "0.2"
|
||||||
|
|
||||||
i8 = _binary.i8
|
|
||||||
i16 = _binary.i16le
|
|
||||||
i32 = _binary.i32le
|
|
||||||
o8 = _binary.o8
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# decoder
|
# decoder
|
||||||
|
|
|
@ -14,8 +14,10 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from PIL import Image, _binary
|
from . import Image, _binary
|
||||||
|
|
||||||
WIDTH = 800
|
WIDTH = 800
|
||||||
|
|
||||||
|
@ -88,7 +90,7 @@ class FontFile(object):
|
||||||
x = xx
|
x = xx
|
||||||
s = src[0] + x0, src[1] + y0, src[2] + x0, src[3] + y0
|
s = src[0] + x0, src[1] + y0, src[2] + x0, src[3] + y0
|
||||||
self.bitmap.paste(im.crop(src), s)
|
self.bitmap.paste(im.crop(src), s)
|
||||||
# print chr(i), dst, s
|
# print(chr(i), dst, s)
|
||||||
self.metrics[i] = d, dst, s
|
self.metrics[i] = d, dst, s
|
||||||
|
|
||||||
def save(self, filename):
|
def save(self, filename):
|
||||||
|
@ -100,14 +102,13 @@ class FontFile(object):
|
||||||
self.bitmap.save(os.path.splitext(filename)[0] + ".pbm", "PNG")
|
self.bitmap.save(os.path.splitext(filename)[0] + ".pbm", "PNG")
|
||||||
|
|
||||||
# font metrics
|
# font metrics
|
||||||
fp = open(os.path.splitext(filename)[0] + ".pil", "wb")
|
with open(os.path.splitext(filename)[0] + ".pil", "wb") as fp:
|
||||||
fp.write(b"PILfont\n")
|
fp.write(b"PILfont\n")
|
||||||
fp.write((";;;;;;%d;\n" % self.ysize).encode('ascii')) # HACK!!!
|
fp.write((";;;;;;%d;\n" % self.ysize).encode('ascii')) # HACK!!!
|
||||||
fp.write(b"DATA\n")
|
fp.write(b"DATA\n")
|
||||||
for id in range(256):
|
for id in range(256):
|
||||||
m = self.metrics[id]
|
m = self.metrics[id]
|
||||||
if not m:
|
if not m:
|
||||||
puti16(fp, [0] * 10)
|
puti16(fp, [0] * 10)
|
||||||
else:
|
else:
|
||||||
puti16(fp, m[0] + m[1] + m[2])
|
puti16(fp, m[0] + m[1] + m[2])
|
||||||
fp.close()
|
|
||||||
|
|
|
@ -15,13 +15,15 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
from PIL import Image, ImageFile
|
from . import Image, ImageFile
|
||||||
from PIL.OleFileIO import i8, i32, MAGIC, OleFileIO
|
from ._binary import i32le as i32, i8
|
||||||
|
|
||||||
|
import olefile
|
||||||
|
|
||||||
__version__ = "0.1"
|
__version__ = "0.1"
|
||||||
|
|
||||||
|
|
||||||
# we map from colour field tuples to (mode, rawmode) descriptors
|
# we map from colour field tuples to (mode, rawmode) descriptors
|
||||||
MODES = {
|
MODES = {
|
||||||
# opacity
|
# opacity
|
||||||
|
@ -42,7 +44,7 @@ MODES = {
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
|
|
||||||
def _accept(prefix):
|
def _accept(prefix):
|
||||||
return prefix[:8] == MAGIC
|
return prefix[:8] == olefile.MAGIC
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -59,7 +61,7 @@ class FpxImageFile(ImageFile.ImageFile):
|
||||||
# to be a FlashPix file
|
# to be a FlashPix file
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.ole = OleFileIO(self.fp)
|
self.ole = olefile.OleFileIO(self.fp)
|
||||||
except IOError:
|
except IOError:
|
||||||
raise SyntaxError("not an FPX file; invalid OLE file")
|
raise SyntaxError("not an FPX file; invalid OLE file")
|
||||||
|
|
||||||
|
@ -112,7 +114,7 @@ class FpxImageFile(ImageFile.ImageFile):
|
||||||
if id in prop:
|
if id in prop:
|
||||||
self.jpeg[i] = prop[id]
|
self.jpeg[i] = prop[id]
|
||||||
|
|
||||||
# print len(self.jpeg), "tables loaded"
|
# print(len(self.jpeg), "tables loaded")
|
||||||
|
|
||||||
self._open_subimage(1, self.maxid)
|
self._open_subimage(1, self.maxid)
|
||||||
|
|
||||||
|
@ -141,7 +143,7 @@ class FpxImageFile(ImageFile.ImageFile):
|
||||||
offset = i32(s, 28)
|
offset = i32(s, 28)
|
||||||
length = i32(s, 32)
|
length = i32(s, 32)
|
||||||
|
|
||||||
# print size, self.mode, self.rawmode
|
# print(size, self.mode, self.rawmode)
|
||||||
|
|
||||||
if size != self.size:
|
if size != self.size:
|
||||||
raise IOError("subimage mismatch")
|
raise IOError("subimage mismatch")
|
||||||
|
|
|
@ -13,7 +13,7 @@ packed custom format called FTEX. This file format uses file extensions FTC and
|
||||||
* FTC files are compressed textures (using standard texture compression).
|
* FTC files are compressed textures (using standard texture compression).
|
||||||
* FTU files are not compressed.
|
* FTU files are not compressed.
|
||||||
Texture File Format
|
Texture File Format
|
||||||
The FTC and FTU texture files both use the same format, called. This
|
The FTC and FTU texture files both use the same format. This
|
||||||
has the following structure:
|
has the following structure:
|
||||||
{header}
|
{header}
|
||||||
{format_directory}
|
{format_directory}
|
||||||
|
@ -42,7 +42,7 @@ Note: All data is stored in little-Endian (Intel) byte order.
|
||||||
|
|
||||||
import struct
|
import struct
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from PIL import Image, ImageFile
|
from . import Image, ImageFile
|
||||||
|
|
||||||
|
|
||||||
MAGIC = b"FTEX"
|
MAGIC = b"FTEX"
|
||||||
|
|
|
@ -24,9 +24,8 @@
|
||||||
# Version 3 files have a format specifier of 18 for 16bit floats in
|
# Version 3 files have a format specifier of 18 for 16bit floats in
|
||||||
# the color depth field. This is currently unsupported by Pillow.
|
# the color depth field. This is currently unsupported by Pillow.
|
||||||
|
|
||||||
from PIL import Image, ImageFile, _binary
|
from . import Image, ImageFile
|
||||||
|
from ._binary import i32be as i32
|
||||||
i32 = _binary.i32be
|
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix):
|
def _accept(prefix):
|
||||||
|
|
|
@ -23,8 +23,9 @@
|
||||||
# purposes only.
|
# purposes only.
|
||||||
|
|
||||||
|
|
||||||
from PIL import ImageFile, ImagePalette, _binary
|
from . import ImageFile, ImagePalette
|
||||||
from PIL._util import isPath
|
from ._binary import i16be as i16
|
||||||
|
from ._util import isPath
|
||||||
|
|
||||||
__version__ = "0.1"
|
__version__ = "0.1"
|
||||||
|
|
||||||
|
@ -34,8 +35,6 @@ except ImportError:
|
||||||
import __builtin__
|
import __builtin__
|
||||||
builtins = __builtin__
|
builtins = __builtin__
|
||||||
|
|
||||||
i16 = _binary.i16be
|
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Image plugin for the GD uncompressed format. Note that this format
|
# Image plugin for the GD uncompressed format. Note that this format
|
||||||
|
|
|
@ -24,8 +24,9 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image, ImageFile, ImagePalette, \
|
from . import Image, ImageFile, ImagePalette, \
|
||||||
ImageChops, ImageSequence, _binary
|
ImageChops, ImageSequence
|
||||||
|
from ._binary import i8, i16le as i16, o8, o16le as o16
|
||||||
|
|
||||||
__version__ = "0.9"
|
__version__ = "0.9"
|
||||||
|
|
||||||
|
@ -33,11 +34,6 @@ __version__ = "0.9"
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Helpers
|
# Helpers
|
||||||
|
|
||||||
i8 = _binary.i8
|
|
||||||
i16 = _binary.i16le
|
|
||||||
o8 = _binary.o8
|
|
||||||
o16 = _binary.o16le
|
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Identify/read GIF files
|
# Identify/read GIF files
|
||||||
|
@ -352,10 +348,18 @@ def _save(im, fp, filename, save_all=False):
|
||||||
|
|
||||||
first_frame = None
|
first_frame = None
|
||||||
append_images = im.encoderinfo.get("append_images", [])
|
append_images = im.encoderinfo.get("append_images", [])
|
||||||
|
if "duration" in im.encoderinfo:
|
||||||
|
duration = im.encoderinfo["duration"]
|
||||||
|
else:
|
||||||
|
duration = None
|
||||||
|
frame_count = 0
|
||||||
for imSequence in [im]+append_images:
|
for imSequence in [im]+append_images:
|
||||||
for im_frame in ImageSequence.Iterator(imSequence):
|
for im_frame in ImageSequence.Iterator(imSequence):
|
||||||
encoderinfo = im.encoderinfo.copy()
|
encoderinfo = im.encoderinfo.copy()
|
||||||
im_frame = _convert_mode(im_frame)
|
im_frame = _convert_mode(im_frame)
|
||||||
|
if isinstance(duration, (list, tuple)):
|
||||||
|
encoderinfo["duration"] = duration[frame_count]
|
||||||
|
frame_count += 1
|
||||||
|
|
||||||
# To specify duration, add the time in milliseconds to getdata(),
|
# To specify duration, add the time in milliseconds to getdata(),
|
||||||
# e.g. getdata(im_frame, duration=1000)
|
# e.g. getdata(im_frame, duration=1000)
|
||||||
|
@ -434,18 +438,16 @@ def _get_local_header(fp, im, offset, flags):
|
||||||
# optimize the block away if transparent color is not used
|
# optimize the block away if transparent color is not used
|
||||||
transparent_color_exists = True
|
transparent_color_exists = True
|
||||||
|
|
||||||
if _get_optimize(im, im.encoderinfo):
|
used_palette_colors = _get_optimize(im, im.encoderinfo)
|
||||||
used_palette_colors = _get_used_palette_colors(im)
|
if used_palette_colors is not None:
|
||||||
|
|
||||||
# adjust the transparency index after optimize
|
# adjust the transparency index after optimize
|
||||||
if len(used_palette_colors) < 256:
|
for i, palette_color in enumerate(used_palette_colors):
|
||||||
for i in range(len(used_palette_colors)):
|
if palette_color == transparency:
|
||||||
if used_palette_colors[i] == transparency:
|
transparency = i
|
||||||
transparency = i
|
transparent_color_exists = True
|
||||||
transparent_color_exists = True
|
break
|
||||||
break
|
else:
|
||||||
else:
|
transparent_color_exists = False
|
||||||
transparent_color_exists = False
|
|
||||||
|
|
||||||
if "duration" in im.encoderinfo:
|
if "duration" in im.encoderinfo:
|
||||||
duration = int(im.encoderinfo["duration"] / 10)
|
duration = int(im.encoderinfo["duration"] / 10)
|
||||||
|
@ -515,22 +517,20 @@ def _save_netpbm(im, fp, filename):
|
||||||
import tempfile
|
import tempfile
|
||||||
file = im._dump()
|
file = im._dump()
|
||||||
|
|
||||||
if im.mode != "RGB":
|
with open(filename, 'wb') as f:
|
||||||
with open(filename, 'wb') as f:
|
if im.mode != "RGB":
|
||||||
stderr = tempfile.TemporaryFile()
|
with tempfile.TemporaryFile() as stderr:
|
||||||
check_call(["ppmtogif", file], stdout=f, stderr=stderr)
|
check_call(["ppmtogif", file], stdout=f, stderr=stderr)
|
||||||
else:
|
else:
|
||||||
with open(filename, 'wb') as f:
|
|
||||||
|
|
||||||
# Pipe ppmquant output into ppmtogif
|
# Pipe ppmquant output into ppmtogif
|
||||||
# "ppmquant 256 %s | ppmtogif > %s" % (file, filename)
|
# "ppmquant 256 %s | ppmtogif > %s" % (file, filename)
|
||||||
quant_cmd = ["ppmquant", "256", file]
|
quant_cmd = ["ppmquant", "256", file]
|
||||||
togif_cmd = ["ppmtogif"]
|
togif_cmd = ["ppmtogif"]
|
||||||
stderr = tempfile.TemporaryFile()
|
with tempfile.TemporaryFile() as stderr:
|
||||||
quant_proc = Popen(quant_cmd, stdout=PIPE, stderr=stderr)
|
quant_proc = Popen(quant_cmd, stdout=PIPE, stderr=stderr)
|
||||||
stderr = tempfile.TemporaryFile()
|
with tempfile.TemporaryFile() as stderr:
|
||||||
togif_proc = Popen(togif_cmd, stdin=quant_proc.stdout, stdout=f,
|
togif_proc = Popen(togif_cmd, stdin=quant_proc.stdout,
|
||||||
stderr=stderr)
|
stdout=f, stderr=stderr)
|
||||||
|
|
||||||
# Allow ppmquant to receive SIGPIPE if ppmtogif exits
|
# Allow ppmquant to receive SIGPIPE if ppmtogif exits
|
||||||
quant_proc.stdout.close()
|
quant_proc.stdout.close()
|
||||||
|
@ -552,9 +552,28 @@ def _save_netpbm(im, fp, filename):
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# GIF utilities
|
# GIF utilities
|
||||||
|
|
||||||
def _get_optimize(im, info):
|
# Force optimization so that we can test performance against
|
||||||
return im.mode in ("P", "L") and info and info.get("optimize", 0)
|
# cases where it took lots of memory and time previously.
|
||||||
|
_FORCE_OPTIMIZE = False
|
||||||
|
|
||||||
|
def _get_optimize(im, info):
|
||||||
|
if im.mode in ("P", "L") and info and info.get("optimize", 0):
|
||||||
|
# Potentially expensive operation.
|
||||||
|
|
||||||
|
# The palette saves 3 bytes per color not used, but palette
|
||||||
|
# lengths are restricted to 3*(2**N) bytes. Max saving would
|
||||||
|
# be 768 -> 6 bytes if we went all the way down to 2 colors.
|
||||||
|
# * If we're over 128 colors, we can't save any space.
|
||||||
|
# * If there aren't any holes, it's not worth collapsing.
|
||||||
|
# * If we have a 'large' image, the palette is in the noise.
|
||||||
|
|
||||||
|
# create the new palette if not every color is used
|
||||||
|
used_palette_colors = _get_used_palette_colors(im)
|
||||||
|
if _FORCE_OPTIMIZE or im.mode == 'L' or \
|
||||||
|
(len(used_palette_colors) <= 128 and
|
||||||
|
max(used_palette_colors) > len(used_palette_colors) and
|
||||||
|
im.width * im.height < 512 * 512):
|
||||||
|
return used_palette_colors
|
||||||
|
|
||||||
def _get_used_palette_colors(im):
|
def _get_used_palette_colors(im):
|
||||||
used_palette_colors = []
|
used_palette_colors = []
|
||||||
|
@ -586,10 +605,6 @@ def _get_header_palette(palette_bytes):
|
||||||
palette_bytes += o8(0) * 3 * actual_target_size_diff
|
palette_bytes += o8(0) * 3 * actual_target_size_diff
|
||||||
return palette_bytes
|
return palette_bytes
|
||||||
|
|
||||||
# Force optimization so that we can test performance against
|
|
||||||
# cases where it took lots of memory and time previously.
|
|
||||||
_FORCE_OPTIMIZE = False
|
|
||||||
|
|
||||||
def _get_palette_bytes(im, palette, info):
|
def _get_palette_bytes(im, palette, info):
|
||||||
if im.mode == "P":
|
if im.mode == "P":
|
||||||
if palette and isinstance(palette, bytes):
|
if palette and isinstance(palette, bytes):
|
||||||
|
@ -600,81 +615,66 @@ def _get_palette_bytes(im, palette, info):
|
||||||
if palette and isinstance(palette, bytes):
|
if palette and isinstance(palette, bytes):
|
||||||
source_palette = palette[:768]
|
source_palette = palette[:768]
|
||||||
else:
|
else:
|
||||||
source_palette = bytearray([i//3 for i in range(768)])
|
source_palette = bytearray(i//3 for i in range(768))
|
||||||
|
|
||||||
used_palette_colors = palette_bytes = None
|
palette_bytes = None
|
||||||
|
|
||||||
if _get_optimize(im, info):
|
used_palette_colors = _get_optimize(im, info)
|
||||||
used_palette_colors = _get_used_palette_colors(im)
|
if used_palette_colors is not None:
|
||||||
|
palette_bytes = b""
|
||||||
|
new_positions = [0]*256
|
||||||
|
|
||||||
# Potentially expensive operation.
|
# pick only the used colors from the palette
|
||||||
|
for i, oldPosition in enumerate(used_palette_colors):
|
||||||
|
palette_bytes += source_palette[oldPosition*3:oldPosition*3+3]
|
||||||
|
new_positions[oldPosition] = i
|
||||||
|
|
||||||
# The palette saves 3 bytes per color not used, but palette
|
# replace the palette color id of all pixel with the new id
|
||||||
# lengths are restricted to 3*(2**N) bytes. Max saving would
|
|
||||||
# be 768 -> 6 bytes if we went all the way down to 2 colors.
|
|
||||||
# * If we're over 128 colors, we can't save any space.
|
|
||||||
# * If there aren't any holes, it's not worth collapsing.
|
|
||||||
# * If we have a 'large' image, the palette is in the noise.
|
|
||||||
|
|
||||||
# create the new palette if not every color is used
|
# Palette images are [0..255], mapped through a 1 or 3
|
||||||
if _FORCE_OPTIMIZE or im.mode == 'L' or \
|
# byte/color map. We need to remap the whole image
|
||||||
(len(used_palette_colors) <= 128 and
|
# from palette 1 to palette 2. New_positions is
|
||||||
max(used_palette_colors) > len(used_palette_colors) and
|
# an array of indexes into palette 1. Palette 2 is
|
||||||
im.width * im.height < 512 * 512):
|
# palette 1 with any holes removed.
|
||||||
palette_bytes = b""
|
|
||||||
new_positions = [0]*256
|
|
||||||
|
|
||||||
# pick only the used colors from the palette
|
# We're going to leverage the convert mechanism to use the
|
||||||
for i, oldPosition in enumerate(used_palette_colors):
|
# C code to remap the image from palette 1 to palette 2,
|
||||||
palette_bytes += source_palette[oldPosition*3:oldPosition*3+3]
|
# by forcing the source image into 'L' mode and adding a
|
||||||
new_positions[oldPosition] = i
|
# mapping 'L' mode palette, then converting back to 'L'
|
||||||
|
# sans palette thus converting the image bytes, then
|
||||||
|
# assigning the optimized RGB palette.
|
||||||
|
|
||||||
# replace the palette color id of all pixel with the new id
|
# perf reference, 9500x4000 gif, w/~135 colors
|
||||||
|
# 14 sec prepatch, 1 sec postpatch with optimization forced.
|
||||||
|
|
||||||
# Palette images are [0..255], mapped through a 1 or 3
|
mapping_palette = bytearray(new_positions)
|
||||||
# byte/color map. We need to remap the whole image
|
|
||||||
# from palette 1 to palette 2. New_positions is
|
|
||||||
# an array of indexes into palette 1. Palette 2 is
|
|
||||||
# palette 1 with any holes removed.
|
|
||||||
|
|
||||||
# We're going to leverage the convert mechanism to use the
|
m_im = im.copy()
|
||||||
# C code to remap the image from palette 1 to palette 2,
|
m_im.mode = 'P'
|
||||||
# by forcing the source image into 'L' mode and adding a
|
|
||||||
# mapping 'L' mode palette, then converting back to 'L'
|
|
||||||
# sans palette thus converting the image bytes, then
|
|
||||||
# assigning the optimized RGB palette.
|
|
||||||
|
|
||||||
# perf reference, 9500x4000 gif, w/~135 colors
|
m_im.palette = ImagePalette.ImagePalette("RGB",
|
||||||
# 14 sec prepatch, 1 sec postpatch with optimization forced.
|
palette=mapping_palette*3,
|
||||||
|
size=768)
|
||||||
|
#possibly set palette dirty, then
|
||||||
|
#m_im.putpalette(mapping_palette, 'L') # converts to 'P'
|
||||||
|
# or just force it.
|
||||||
|
# UNDONE -- this is part of the general issue with palettes
|
||||||
|
m_im.im.putpalette(*m_im.palette.getdata())
|
||||||
|
|
||||||
mapping_palette = bytearray(new_positions)
|
m_im = m_im.convert('L')
|
||||||
|
|
||||||
m_im = im.copy()
|
# Internally, we require 768 bytes for a palette.
|
||||||
m_im.mode = 'P'
|
new_palette_bytes = (palette_bytes +
|
||||||
|
(768 - len(palette_bytes)) * b'\x00')
|
||||||
|
m_im.putpalette(new_palette_bytes)
|
||||||
|
m_im.palette = ImagePalette.ImagePalette("RGB",
|
||||||
|
palette=palette_bytes,
|
||||||
|
size=len(palette_bytes))
|
||||||
|
|
||||||
m_im.palette = ImagePalette.ImagePalette("RGB",
|
# oh gawd, this is modifying the image in place so I can pass by ref.
|
||||||
palette=mapping_palette*3,
|
# REFACTOR SOONEST
|
||||||
size=768)
|
im.frombytes(m_im.tobytes())
|
||||||
#possibly set palette dirty, then
|
|
||||||
#m_im.putpalette(mapping_palette, 'L') # converts to 'P'
|
|
||||||
# or just force it.
|
|
||||||
# UNDONE -- this is part of the general issue with palettes
|
|
||||||
m_im.im.putpalette(*m_im.palette.getdata())
|
|
||||||
|
|
||||||
m_im = m_im.convert('L')
|
|
||||||
|
|
||||||
# Internally, we require 768 bytes for a palette.
|
|
||||||
new_palette_bytes = (palette_bytes +
|
|
||||||
(768 - len(palette_bytes)) * b'\x00')
|
|
||||||
m_im.putpalette(new_palette_bytes)
|
|
||||||
m_im.palette = ImagePalette.ImagePalette("RGB",
|
|
||||||
palette=palette_bytes,
|
|
||||||
size=len(palette_bytes))
|
|
||||||
|
|
||||||
# oh gawd, this is modifying the image in place so I can pass by ref.
|
|
||||||
# REFACTOR SOONEST
|
|
||||||
im.frombytes(m_im.tobytes())
|
|
||||||
|
|
||||||
if not palette_bytes:
|
if not palette_bytes:
|
||||||
palette_bytes = source_palette
|
palette_bytes = source_palette
|
||||||
|
|
||||||
|
@ -696,7 +696,7 @@ def getheader(im, palette=None, info=None):
|
||||||
version = b"89a"
|
version = b"89a"
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
if im.info.get("version") == "89a":
|
if im.info.get("version") == b"89a":
|
||||||
version = b"89a"
|
version = b"89a"
|
||||||
|
|
||||||
header = [
|
header = [
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
from math import pi, log, sin, sqrt
|
from math import pi, log, sin, sqrt
|
||||||
from PIL._binary import o8
|
from ._binary import o8
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Stuff to translate curve segments to palette values (derived from
|
# Stuff to translate curve segments to palette values (derived from
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from PIL._binary import o8
|
from ._binary import o8
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -41,7 +41,7 @@ class GimpPaletteFile(object):
|
||||||
if not s:
|
if not s:
|
||||||
break
|
break
|
||||||
# skip fields and comment lines
|
# skip fields and comment lines
|
||||||
if re.match(b"\w+:|#", s):
|
if re.match(br"\w+:|#", s):
|
||||||
continue
|
continue
|
||||||
if len(s) > 100:
|
if len(s) > 100:
|
||||||
raise SyntaxError("bad palette file")
|
raise SyntaxError("bad palette file")
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image, ImageFile
|
from . import Image, ImageFile
|
||||||
|
|
||||||
_handler = None
|
_handler = None
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image, ImageFile
|
from . import Image, ImageFile
|
||||||
|
|
||||||
_handler = None
|
_handler = None
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image, ImageFile, PngImagePlugin, _binary
|
from PIL import Image, ImageFile, PngImagePlugin
|
||||||
|
from PIL._binary import i8
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
@ -27,8 +28,6 @@ enable_jpeg2k = hasattr(Image.core, 'jp2klib_version')
|
||||||
if enable_jpeg2k:
|
if enable_jpeg2k:
|
||||||
from PIL import Jpeg2KImagePlugin
|
from PIL import Jpeg2KImagePlugin
|
||||||
|
|
||||||
i8 = _binary.i8
|
|
||||||
|
|
||||||
HEADERSIZE = 8
|
HEADERSIZE = 8
|
||||||
|
|
||||||
|
|
||||||
|
@ -330,8 +329,8 @@ def _save(im, fp, filename):
|
||||||
from subprocess import Popen, PIPE, CalledProcessError
|
from subprocess import Popen, PIPE, CalledProcessError
|
||||||
|
|
||||||
convert_cmd = ["iconutil", "-c", "icns", "-o", filename, iconset]
|
convert_cmd = ["iconutil", "-c", "icns", "-o", filename, iconset]
|
||||||
stderr = tempfile.TemporaryFile()
|
with tempfile.TemporaryFile() as stderr:
|
||||||
convert_proc = Popen(convert_cmd, stdout=PIPE, stderr=stderr)
|
convert_proc = Popen(convert_cmd, stdout=PIPE, stderr=stderr)
|
||||||
|
|
||||||
convert_proc.stdout.close()
|
convert_proc.stdout.close()
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,8 @@
|
||||||
import struct
|
import struct
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
from PIL import Image, ImageFile, BmpImagePlugin, PngImagePlugin, _binary
|
from . import Image, ImageFile, BmpImagePlugin, PngImagePlugin
|
||||||
|
from ._binary import i8, i16le as i16, i32le as i32
|
||||||
from math import log, ceil
|
from math import log, ceil
|
||||||
|
|
||||||
__version__ = "0.1"
|
__version__ = "0.1"
|
||||||
|
@ -33,10 +34,6 @@ __version__ = "0.1"
|
||||||
#
|
#
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
|
|
||||||
i8 = _binary.i8
|
|
||||||
i16 = _binary.i16le
|
|
||||||
i32 = _binary.i32le
|
|
||||||
|
|
||||||
_MAGIC = b"\0\0\1\0"
|
_MAGIC = b"\0\0\1\0"
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,16 +41,19 @@ def _save(im, fp, filename):
|
||||||
fp.write(_MAGIC) # (2+2)
|
fp.write(_MAGIC) # (2+2)
|
||||||
sizes = im.encoderinfo.get("sizes",
|
sizes = im.encoderinfo.get("sizes",
|
||||||
[(16, 16), (24, 24), (32, 32), (48, 48),
|
[(16, 16), (24, 24), (32, 32), (48, 48),
|
||||||
(64, 64), (128, 128), (255, 255)])
|
(64, 64), (128, 128), (256, 256)])
|
||||||
width, height = im.size
|
width, height = im.size
|
||||||
filter(lambda x: False if (x[0] > width or x[1] > height or
|
sizes = filter(lambda x: False if (x[0] > width or x[1] > height or
|
||||||
x[0] > 255 or x[1] > 255) else True, sizes)
|
x[0] > 256 or x[1] > 256) else True,
|
||||||
|
sizes)
|
||||||
|
sizes = list(sizes)
|
||||||
fp.write(struct.pack("<H", len(sizes))) # idCount(2)
|
fp.write(struct.pack("<H", len(sizes))) # idCount(2)
|
||||||
offset = fp.tell() + len(sizes)*16
|
offset = fp.tell() + len(sizes)*16
|
||||||
for size in sizes:
|
for size in sizes:
|
||||||
width, height = size
|
width, height = size
|
||||||
fp.write(struct.pack("B", width)) # bWidth(1)
|
# 0 means 256
|
||||||
fp.write(struct.pack("B", height)) # bHeight(1)
|
fp.write(struct.pack("B", width if width < 256 else 0)) # bWidth(1)
|
||||||
|
fp.write(struct.pack("B", height if height < 256 else 0)) # bHeight(1)
|
||||||
fp.write(b"\0") # bColorCount(1)
|
fp.write(b"\0") # bColorCount(1)
|
||||||
fp.write(b"\0") # bReserved(1)
|
fp.write(b"\0") # bReserved(1)
|
||||||
fp.write(b"\0\0") # wPlanes(2)
|
fp.write(b"\0\0") # wPlanes(2)
|
||||||
|
@ -139,7 +139,7 @@ class IcoFile(object):
|
||||||
"""
|
"""
|
||||||
Get a list of all available icon sizes and color depths.
|
Get a list of all available icon sizes and color depths.
|
||||||
"""
|
"""
|
||||||
return set((h['width'], h['height']) for h in self.entry)
|
return {(h['width'], h['height']) for h in self.entry}
|
||||||
|
|
||||||
def getimage(self, size, bpp=False):
|
def getimage(self, size, bpp=False):
|
||||||
"""
|
"""
|
||||||
|
@ -215,13 +215,13 @@ class IcoFile(object):
|
||||||
total_bytes = int((w * im.size[1]) / 8)
|
total_bytes = int((w * im.size[1]) / 8)
|
||||||
|
|
||||||
self.buf.seek(and_mask_offset)
|
self.buf.seek(and_mask_offset)
|
||||||
maskData = self.buf.read(total_bytes)
|
mask_data = self.buf.read(total_bytes)
|
||||||
|
|
||||||
# convert raw data to image
|
# convert raw data to image
|
||||||
mask = Image.frombuffer(
|
mask = Image.frombuffer(
|
||||||
'1', # 1 bpp
|
'1', # 1 bpp
|
||||||
im.size, # (w, h)
|
im.size, # (w, h)
|
||||||
maskData, # source chars
|
mask_data, # source chars
|
||||||
'raw', # raw decoder
|
'raw', # raw decoder
|
||||||
('1;I', int(w/8), -1) # 1bpp inverted, padded, reversed
|
('1;I', int(w/8), -1) # 1bpp inverted, padded, reversed
|
||||||
)
|
)
|
||||||
|
@ -278,6 +278,7 @@ class IcoImageFile(ImageFile.ImageFile):
|
||||||
#
|
#
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
Image.register_open(IcoImageFile.format, IcoImageFile, _accept)
|
Image.register_open(IcoImageFile.format, IcoImageFile, _accept)
|
||||||
Image.register_save(IcoImageFile.format, _save)
|
Image.register_save(IcoImageFile.format, _save)
|
||||||
Image.register_extension(IcoImageFile.format, ".ico")
|
Image.register_extension(IcoImageFile.format, ".ico")
|
||||||
|
|
|
@ -27,8 +27,8 @@
|
||||||
|
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from PIL import Image, ImageFile, ImagePalette
|
from . import Image, ImageFile, ImagePalette
|
||||||
from PIL._binary import i8
|
from ._binary import i8
|
||||||
|
|
||||||
__version__ = "0.7"
|
__version__ = "0.7"
|
||||||
|
|
||||||
|
|
176
PIL/Image.py
176
PIL/Image.py
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
from PIL import VERSION, PILLOW_VERSION, _plugins
|
from . import VERSION, PILLOW_VERSION, _plugins
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import warnings
|
import warnings
|
||||||
|
@ -46,7 +46,7 @@ class _imaging_not_installed(object):
|
||||||
|
|
||||||
|
|
||||||
# Limit to around a quarter gigabyte for a 24 bit (3 bpp) image
|
# Limit to around a quarter gigabyte for a 24 bit (3 bpp) image
|
||||||
MAX_IMAGE_PIXELS = int(1024 * 1024 * 1024 / 4 / 3)
|
MAX_IMAGE_PIXELS = int(1024 * 1024 * 1024 // 4 // 3)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# give Tk a chance to set up the environment, in case we're
|
# give Tk a chance to set up the environment, in case we're
|
||||||
|
@ -64,10 +64,10 @@ try:
|
||||||
# import Image and use the Image.core variable instead.
|
# import Image and use the Image.core variable instead.
|
||||||
# Also note that Image.core is not a publicly documented interface,
|
# Also note that Image.core is not a publicly documented interface,
|
||||||
# and should be considered private and subject to change.
|
# and should be considered private and subject to change.
|
||||||
from PIL import _imaging as core
|
from . import _imaging as core
|
||||||
if PILLOW_VERSION != getattr(core, 'PILLOW_VERSION', None):
|
if PILLOW_VERSION != getattr(core, 'PILLOW_VERSION', None):
|
||||||
raise ImportError("The _imaging extension was built for another "
|
raise ImportError("The _imaging extension was built for another "
|
||||||
" version of Pillow or PIL")
|
"version of Pillow or PIL")
|
||||||
|
|
||||||
except ImportError as v:
|
except ImportError as v:
|
||||||
core = _imaging_not_installed()
|
core = _imaging_not_installed()
|
||||||
|
@ -109,11 +109,9 @@ except ImportError:
|
||||||
import __builtin__
|
import __builtin__
|
||||||
builtins = __builtin__
|
builtins = __builtin__
|
||||||
|
|
||||||
from PIL import ImageMode
|
from . import ImageMode
|
||||||
from PIL._binary import i8
|
from ._binary import i8
|
||||||
from PIL._util import isPath
|
from ._util import isPath, isStringType, deferred_error
|
||||||
from PIL._util import isStringType
|
|
||||||
from PIL._util import deferred_error
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
@ -355,23 +353,23 @@ def preinit():
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from PIL import BmpImagePlugin
|
from . import BmpImagePlugin
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
from PIL import GifImagePlugin
|
from . import GifImagePlugin
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
from PIL import JpegImagePlugin
|
from . import JpegImagePlugin
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
from PIL import PpmImagePlugin
|
from . import PpmImagePlugin
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
from PIL import PngImagePlugin
|
from . import PngImagePlugin
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
# try:
|
# try:
|
||||||
|
@ -525,13 +523,11 @@ class Image(object):
|
||||||
if self.palette:
|
if self.palette:
|
||||||
new.palette = self.palette.copy()
|
new.palette = self.palette.copy()
|
||||||
if im.mode == "P" and not new.palette:
|
if im.mode == "P" and not new.palette:
|
||||||
from PIL import ImagePalette
|
from . import ImagePalette
|
||||||
new.palette = ImagePalette.ImagePalette()
|
new.palette = ImagePalette.ImagePalette()
|
||||||
new.info = self.info.copy()
|
new.info = self.info.copy()
|
||||||
return new
|
return new
|
||||||
|
|
||||||
_makeself = _new # compatibility
|
|
||||||
|
|
||||||
# Context Manager Support
|
# Context Manager Support
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
return self
|
return self
|
||||||
|
@ -777,7 +773,7 @@ class Image(object):
|
||||||
if HAS_CFFI and USE_CFFI_ACCESS:
|
if HAS_CFFI and USE_CFFI_ACCESS:
|
||||||
if self.pyaccess:
|
if self.pyaccess:
|
||||||
return self.pyaccess
|
return self.pyaccess
|
||||||
from PIL import PyAccess
|
from . import PyAccess
|
||||||
self.pyaccess = PyAccess.new(self, self.readonly)
|
self.pyaccess = PyAccess.new(self, self.readonly)
|
||||||
if self.pyaccess:
|
if self.pyaccess:
|
||||||
return self.pyaccess
|
return self.pyaccess
|
||||||
|
@ -879,7 +875,7 @@ class Image(object):
|
||||||
trns_im = Image()._new(core.new(self.mode, (1, 1)))
|
trns_im = Image()._new(core.new(self.mode, (1, 1)))
|
||||||
if self.mode == 'P':
|
if self.mode == 'P':
|
||||||
trns_im.putpalette(self.palette)
|
trns_im.putpalette(self.palette)
|
||||||
if type(t) == tuple:
|
if isinstance(t, tuple):
|
||||||
try:
|
try:
|
||||||
t = trns_im.palette.getcolor(t)
|
t = trns_im.palette.getcolor(t)
|
||||||
except:
|
except:
|
||||||
|
@ -910,7 +906,7 @@ class Image(object):
|
||||||
if mode == "P" and palette == ADAPTIVE:
|
if mode == "P" and palette == ADAPTIVE:
|
||||||
im = self.im.quantize(colors)
|
im = self.im.quantize(colors)
|
||||||
new = self._new(im)
|
new = self._new(im)
|
||||||
from PIL import ImagePalette
|
from . import ImagePalette
|
||||||
new.palette = ImagePalette.raw("RGB", new.im.getpalette("RGB"))
|
new.palette = ImagePalette.raw("RGB", new.im.getpalette("RGB"))
|
||||||
if delete_trns:
|
if delete_trns:
|
||||||
# This could possibly happen if we requantize to fewer colors.
|
# This could possibly happen if we requantize to fewer colors.
|
||||||
|
@ -997,7 +993,7 @@ class Image(object):
|
||||||
"only RGB or L mode images can be quantized to a palette"
|
"only RGB or L mode images can be quantized to a palette"
|
||||||
)
|
)
|
||||||
im = self.im.convert("P", 1, palette.im)
|
im = self.im.convert("P", 1, palette.im)
|
||||||
return self._makeself(im)
|
return self._new(im)
|
||||||
|
|
||||||
return self._new(self.im.quantize(colors, method, kmeans))
|
return self._new(self.im.quantize(colors, method, kmeans))
|
||||||
|
|
||||||
|
@ -1020,7 +1016,7 @@ class Image(object):
|
||||||
4-tuple defining the left, upper, right, and lower pixel
|
4-tuple defining the left, upper, right, and lower pixel
|
||||||
coordinate.
|
coordinate.
|
||||||
|
|
||||||
Note: Prior to Pillow 3.4.0, this was a lazy operation.
|
Note: Prior to Pillow 3.4.0, this was a lazy operation.
|
||||||
|
|
||||||
:param box: The crop rectangle, as a (left, upper, right, lower)-tuple.
|
:param box: The crop rectangle, as a (left, upper, right, lower)-tuple.
|
||||||
:rtype: :py:class:`~PIL.Image.Image`
|
:rtype: :py:class:`~PIL.Image.Image`
|
||||||
|
@ -1307,8 +1303,7 @@ class Image(object):
|
||||||
box = None
|
box = None
|
||||||
|
|
||||||
if box is None:
|
if box is None:
|
||||||
# cover all of self
|
box = (0, 0)
|
||||||
box = (0, 0) + self.size
|
|
||||||
|
|
||||||
if len(box) == 2:
|
if len(box) == 2:
|
||||||
# upper left corner given; get size from image or mask
|
# upper left corner given; get size from image or mask
|
||||||
|
@ -1321,10 +1316,10 @@ class Image(object):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"cannot determine region size; use 4-item box"
|
"cannot determine region size; use 4-item box"
|
||||||
)
|
)
|
||||||
box = box + (box[0]+size[0], box[1]+size[1])
|
box += (box[0]+size[0], box[1]+size[1])
|
||||||
|
|
||||||
if isStringType(im):
|
if isStringType(im):
|
||||||
from PIL import ImageColor
|
from . import ImageColor
|
||||||
im = ImageColor.getcolor(im, self.mode)
|
im = ImageColor.getcolor(im, self.mode)
|
||||||
|
|
||||||
elif isImageType(im):
|
elif isImageType(im):
|
||||||
|
@ -1471,7 +1466,7 @@ class Image(object):
|
||||||
|
|
||||||
:param data: A palette sequence (either a list or a string).
|
:param data: A palette sequence (either a list or a string).
|
||||||
"""
|
"""
|
||||||
from PIL import ImagePalette
|
from . import ImagePalette
|
||||||
|
|
||||||
if self.mode not in ("L", "P"):
|
if self.mode not in ("L", "P"):
|
||||||
raise ValueError("illegal image mode")
|
raise ValueError("illegal image mode")
|
||||||
|
@ -1545,7 +1540,7 @@ class Image(object):
|
||||||
|
|
||||||
size = tuple(size)
|
size = tuple(size)
|
||||||
if self.size == size:
|
if self.size == size:
|
||||||
return self._new(self.im)
|
return self.copy()
|
||||||
|
|
||||||
if self.mode in ("1", "P"):
|
if self.mode in ("1", "P"):
|
||||||
resample = NEAREST
|
resample = NEAREST
|
||||||
|
@ -1558,7 +1553,7 @@ class Image(object):
|
||||||
|
|
||||||
return self._new(self.im.resize(size, resample))
|
return self._new(self.im.resize(size, resample))
|
||||||
|
|
||||||
def rotate(self, angle, resample=NEAREST, expand=0):
|
def rotate(self, angle, resample=NEAREST, expand=0, center=None, translate=None):
|
||||||
"""
|
"""
|
||||||
Returns a rotated copy of this image. This method returns a
|
Returns a rotated copy of this image. This method returns a
|
||||||
copy of this image, rotated the given number of degrees counter
|
copy of this image, rotated the given number of degrees counter
|
||||||
|
@ -1575,48 +1570,81 @@ class Image(object):
|
||||||
:param expand: Optional expansion flag. If true, expands the output
|
:param expand: Optional expansion flag. If true, expands the output
|
||||||
image to make it large enough to hold the entire rotated image.
|
image to make it large enough to hold the entire rotated image.
|
||||||
If false or omitted, make the output image the same size as the
|
If false or omitted, make the output image the same size as the
|
||||||
input image.
|
input image. Note that the expand flag assumes rotation around
|
||||||
|
the center and no translation.
|
||||||
|
:param center: Optional center of rotation (a 2-tuple). Origin is
|
||||||
|
the upper left corner. Default is the center of the image.
|
||||||
|
:param translate: An optional post-rotate translation (a 2-tuple).
|
||||||
:returns: An :py:class:`~PIL.Image.Image` object.
|
:returns: An :py:class:`~PIL.Image.Image` object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
angle = angle % 360.0
|
angle = angle % 360.0
|
||||||
|
|
||||||
# Fast paths regardless of filter
|
# Fast paths regardless of filter, as long as we're not
|
||||||
if angle == 0:
|
# translating or changing the center.
|
||||||
return self.copy()
|
if not (center or translate):
|
||||||
if angle == 180:
|
if angle == 0:
|
||||||
return self.transpose(ROTATE_180)
|
return self.copy()
|
||||||
if angle == 90 and expand:
|
if angle == 180:
|
||||||
return self.transpose(ROTATE_90)
|
return self.transpose(ROTATE_180)
|
||||||
if angle == 270 and expand:
|
if angle == 90 and expand:
|
||||||
return self.transpose(ROTATE_270)
|
return self.transpose(ROTATE_90)
|
||||||
|
if angle == 270 and expand:
|
||||||
|
return self.transpose(ROTATE_270)
|
||||||
|
|
||||||
|
# Calculate the affine matrix. Note that this is the reverse
|
||||||
|
# transformation (from destination image to source) because we
|
||||||
|
# want to interpolate the (discrete) destination pixel from
|
||||||
|
# the local area around the (floating) source pixel.
|
||||||
|
|
||||||
|
# The matrix we actually want (note that it operates from the right):
|
||||||
|
# (1, 0, tx) (1, 0, cx) ( cos a, sin a, 0) (1, 0, -cx)
|
||||||
|
# (0, 1, ty) * (0, 1, cy) * (-sin a, cos a, 0) * (0, 1, -cy)
|
||||||
|
# (0, 0, 1) (0, 0, 1) ( 0, 0, 1) (0, 0, 1)
|
||||||
|
|
||||||
|
# The reverse matrix is thus:
|
||||||
|
# (1, 0, cx) ( cos -a, sin -a, 0) (1, 0, -cx) (1, 0, -tx)
|
||||||
|
# (0, 1, cy) * (-sin -a, cos -a, 0) * (0, 1, -cy) * (0, 1, -ty)
|
||||||
|
# (0, 0, 1) ( 0, 0, 1) (0, 0, 1) (0, 0, 1)
|
||||||
|
|
||||||
|
# In any case, the final translation may be updated at the end to
|
||||||
|
# compensate for the expand flag.
|
||||||
|
|
||||||
|
w, h = self.size
|
||||||
|
|
||||||
|
if translate is None:
|
||||||
|
translate = [0, 0]
|
||||||
|
if center is None:
|
||||||
|
center = [w / 2.0, h / 2.0]
|
||||||
|
|
||||||
angle = - math.radians(angle)
|
angle = - math.radians(angle)
|
||||||
matrix = [
|
matrix = [
|
||||||
round(math.cos(angle), 15), round(math.sin(angle), 15), 0.0,
|
round(math.cos(angle), 15), round(math.sin(angle), 15), 0.0,
|
||||||
round(-math.sin(angle), 15), round(math.cos(angle), 15), 0.0
|
round(-math.sin(angle), 15), round(math.cos(angle), 15), 0.0
|
||||||
]
|
]
|
||||||
|
def transform(x, y, matrix):
|
||||||
def transform(x, y, matrix=matrix):
|
|
||||||
(a, b, c, d, e, f) = matrix
|
(a, b, c, d, e, f) = matrix
|
||||||
return a*x + b*y + c, d*x + e*y + f
|
return a*x + b*y + c, d*x + e*y + f
|
||||||
|
matrix[2], matrix[5] = transform(-center[0] - translate[0], -center[1] - translate[1], matrix)
|
||||||
|
matrix[2] += center[0]
|
||||||
|
matrix[5] += center[1]
|
||||||
|
|
||||||
w, h = self.size
|
|
||||||
if expand:
|
if expand:
|
||||||
# calculate output size
|
# calculate output size
|
||||||
xx = []
|
xx = []
|
||||||
yy = []
|
yy = []
|
||||||
for x, y in ((0, 0), (w, 0), (w, h), (0, h)):
|
for x, y in ((0, 0), (w, 0), (w, h), (0, h)):
|
||||||
x, y = transform(x, y)
|
x, y = transform(x, y, matrix)
|
||||||
xx.append(x)
|
xx.append(x)
|
||||||
yy.append(y)
|
yy.append(y)
|
||||||
w = int(math.ceil(max(xx)) - math.floor(min(xx)))
|
nw = int(math.ceil(max(xx)) - math.floor(min(xx)))
|
||||||
h = int(math.ceil(max(yy)) - math.floor(min(yy)))
|
nh = int(math.ceil(max(yy)) - math.floor(min(yy)))
|
||||||
|
|
||||||
# adjust center
|
# We multiply a translation matrix from the right. Because of its
|
||||||
x, y = transform(w / 2.0, h / 2.0)
|
# special form, this is the same as taking the image of the translation vector
|
||||||
matrix[2] = self.size[0] / 2.0 - x
|
# as new translation vector.
|
||||||
matrix[5] = self.size[1] / 2.0 - y
|
matrix[2], matrix[5] = transform(-(nw - w) / 2.0, -(nh - h) / 2.0, matrix)
|
||||||
|
w, h = nw, nh
|
||||||
|
|
||||||
return self.transform((w, h), AFFINE, matrix, resample)
|
return self.transform((w, h), AFFINE, matrix, resample)
|
||||||
|
|
||||||
|
@ -1660,7 +1688,7 @@ class Image(object):
|
||||||
if isinstance(fp, Path):
|
if isinstance(fp, Path):
|
||||||
filename = str(fp)
|
filename = str(fp)
|
||||||
open_fp = True
|
open_fp = True
|
||||||
elif hasattr(fp, "name") and isPath(fp.name):
|
if not filename and hasattr(fp, "name") and isPath(fp.name):
|
||||||
# only set the name for metadata purposes
|
# only set the name for metadata purposes
|
||||||
filename = fp.name
|
filename = fp.name
|
||||||
|
|
||||||
|
@ -1947,14 +1975,14 @@ class Image(object):
|
||||||
|
|
||||||
def toqimage(self):
|
def toqimage(self):
|
||||||
"""Returns a QImage copy of this image"""
|
"""Returns a QImage copy of this image"""
|
||||||
from PIL import ImageQt
|
from . import ImageQt
|
||||||
if not ImageQt.qt_is_installed:
|
if not ImageQt.qt_is_installed:
|
||||||
raise ImportError("Qt bindings are not installed")
|
raise ImportError("Qt bindings are not installed")
|
||||||
return ImageQt.toqimage(self)
|
return ImageQt.toqimage(self)
|
||||||
|
|
||||||
def toqpixmap(self):
|
def toqpixmap(self):
|
||||||
"""Returns a QPixmap copy of this image"""
|
"""Returns a QPixmap copy of this image"""
|
||||||
from PIL import ImageQt
|
from . import ImageQt
|
||||||
if not ImageQt.qt_is_installed:
|
if not ImageQt.qt_is_installed:
|
||||||
raise ImportError("Qt bindings are not installed")
|
raise ImportError("Qt bindings are not installed")
|
||||||
return ImageQt.toqpixmap(self)
|
return ImageQt.toqpixmap(self)
|
||||||
|
@ -1985,6 +2013,22 @@ def _wedge():
|
||||||
|
|
||||||
return Image()._new(core.wedge("L"))
|
return Image()._new(core.wedge("L"))
|
||||||
|
|
||||||
|
def _check_size(size):
|
||||||
|
"""
|
||||||
|
Common check to enforce type and sanity check on size tuples
|
||||||
|
|
||||||
|
:param size: Should be a 2 tuple of (width, height)
|
||||||
|
:returns: True, or raises a ValueError
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not isinstance(size, (list, tuple)):
|
||||||
|
raise ValueError("Size must be a tuple")
|
||||||
|
if len(size) != 2:
|
||||||
|
raise ValueError("Size must be a tuple of length 2")
|
||||||
|
if size[0] < 0 or size[1] < 0:
|
||||||
|
raise ValueError("Width and Height must be => 0")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def new(mode, size, color=0):
|
def new(mode, size, color=0):
|
||||||
"""
|
"""
|
||||||
|
@ -2002,6 +2046,8 @@ def new(mode, size, color=0):
|
||||||
:returns: An :py:class:`~PIL.Image.Image` object.
|
:returns: An :py:class:`~PIL.Image.Image` object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
_check_size(size)
|
||||||
|
|
||||||
if color is None:
|
if color is None:
|
||||||
# don't initialize
|
# don't initialize
|
||||||
return Image()._new(core.new(mode, size))
|
return Image()._new(core.new(mode, size))
|
||||||
|
@ -2009,7 +2055,7 @@ def new(mode, size, color=0):
|
||||||
if isStringType(color):
|
if isStringType(color):
|
||||||
# css3-style specifier
|
# css3-style specifier
|
||||||
|
|
||||||
from PIL import ImageColor
|
from . import ImageColor
|
||||||
color = ImageColor.getcolor(color, mode)
|
color = ImageColor.getcolor(color, mode)
|
||||||
|
|
||||||
return Image()._new(core.fill(mode, size, color))
|
return Image()._new(core.fill(mode, size, color))
|
||||||
|
@ -2039,6 +2085,8 @@ def frombytes(mode, size, data, decoder_name="raw", *args):
|
||||||
:returns: An :py:class:`~PIL.Image.Image` object.
|
:returns: An :py:class:`~PIL.Image.Image` object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
_check_size(size)
|
||||||
|
|
||||||
# may pass tuple instead of argument list
|
# may pass tuple instead of argument list
|
||||||
if len(args) == 1 and isinstance(args[0], tuple):
|
if len(args) == 1 and isinstance(args[0], tuple):
|
||||||
args = args[0]
|
args = args[0]
|
||||||
|
@ -2091,6 +2139,8 @@ def frombuffer(mode, size, data, decoder_name="raw", *args):
|
||||||
.. versionadded:: 1.1.4
|
.. versionadded:: 1.1.4
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
_check_size(size)
|
||||||
|
|
||||||
# may pass tuple instead of argument list
|
# may pass tuple instead of argument list
|
||||||
if len(args) == 1 and isinstance(args[0], tuple):
|
if len(args) == 1 and isinstance(args[0], tuple):
|
||||||
args = args[0]
|
args = args[0]
|
||||||
|
@ -2142,7 +2192,7 @@ def fromarray(obj, mode=None):
|
||||||
typekey = (1, 1) + shape[2:], arr['typestr']
|
typekey = (1, 1) + shape[2:], arr['typestr']
|
||||||
mode, rawmode = _fromarray_typemap[typekey]
|
mode, rawmode = _fromarray_typemap[typekey]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# print typekey
|
# print(typekey)
|
||||||
raise TypeError("Cannot handle this data type")
|
raise TypeError("Cannot handle this data type")
|
||||||
else:
|
else:
|
||||||
rawmode = mode
|
rawmode = mode
|
||||||
|
@ -2167,7 +2217,7 @@ def fromarray(obj, mode=None):
|
||||||
|
|
||||||
def fromqimage(im):
|
def fromqimage(im):
|
||||||
"""Creates an image instance from a QImage image"""
|
"""Creates an image instance from a QImage image"""
|
||||||
from PIL import ImageQt
|
from . import ImageQt
|
||||||
if not ImageQt.qt_is_installed:
|
if not ImageQt.qt_is_installed:
|
||||||
raise ImportError("Qt bindings are not installed")
|
raise ImportError("Qt bindings are not installed")
|
||||||
return ImageQt.fromqimage(im)
|
return ImageQt.fromqimage(im)
|
||||||
|
@ -2175,7 +2225,7 @@ def fromqimage(im):
|
||||||
|
|
||||||
def fromqpixmap(im):
|
def fromqpixmap(im):
|
||||||
"""Creates an image instance from a QPixmap image"""
|
"""Creates an image instance from a QPixmap image"""
|
||||||
from PIL import ImageQt
|
from . import ImageQt
|
||||||
if not ImageQt.qt_is_installed:
|
if not ImageQt.qt_is_installed:
|
||||||
raise ImportError("Qt bindings are not installed")
|
raise ImportError("Qt bindings are not installed")
|
||||||
return ImageQt.fromqpixmap(im)
|
return ImageQt.fromqpixmap(im)
|
||||||
|
@ -2460,6 +2510,16 @@ def register_extension(id, extension):
|
||||||
EXTENSION[extension.lower()] = id.upper()
|
EXTENSION[extension.lower()] = id.upper()
|
||||||
|
|
||||||
|
|
||||||
|
def registered_extensions():
|
||||||
|
"""
|
||||||
|
Returns a dictionary containing all file extensions belonging
|
||||||
|
to registered plugins
|
||||||
|
"""
|
||||||
|
if not bool(EXTENSION):
|
||||||
|
init()
|
||||||
|
return EXTENSION
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Simple display support. User code may override this.
|
# Simple display support. User code may override this.
|
||||||
|
|
||||||
|
@ -2469,7 +2529,7 @@ def _show(image, **options):
|
||||||
|
|
||||||
|
|
||||||
def _showxv(image, title=None, **options):
|
def _showxv(image, title=None, **options):
|
||||||
from PIL import ImageShow
|
from . import ImageShow
|
||||||
ImageShow.show(image, title, **options)
|
ImageShow.show(image, title, **options)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image
|
from . import Image
|
||||||
|
|
||||||
|
|
||||||
def constant(image, value):
|
def constant(image, value):
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image
|
from . import Image
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,8 +33,8 @@
|
||||||
import numbers
|
import numbers
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from PIL import Image, ImageColor
|
from . import Image, ImageColor
|
||||||
from PIL._util import isStringType
|
from ._util import isStringType
|
||||||
|
|
||||||
"""
|
"""
|
||||||
A simple 2D drawing interface for PIL images.
|
A simple 2D drawing interface for PIL images.
|
||||||
|
@ -105,7 +105,7 @@ class ImageDraw(object):
|
||||||
"""Get the current default font."""
|
"""Get the current default font."""
|
||||||
if not self.font:
|
if not self.font:
|
||||||
# FIXME: should add a font repository
|
# FIXME: should add a font repository
|
||||||
from PIL import ImageFont
|
from . import ImageFont
|
||||||
self.font = ImageFont.load_default()
|
self.font = ImageFont.load_default()
|
||||||
return self.font
|
return self.font
|
||||||
|
|
||||||
|
@ -208,12 +208,12 @@ class ImageDraw(object):
|
||||||
|
|
||||||
def _multiline_check(self, text):
|
def _multiline_check(self, text):
|
||||||
"""Draw text."""
|
"""Draw text."""
|
||||||
split_character = "\n" if isinstance(text, type("")) else b"\n"
|
split_character = "\n" if isinstance(text, str) else b"\n"
|
||||||
|
|
||||||
return split_character in text
|
return split_character in text
|
||||||
|
|
||||||
def _multiline_split(self, text):
|
def _multiline_split(self, text):
|
||||||
split_character = "\n" if isinstance(text, type("")) else b"\n"
|
split_character = "\n" if isinstance(text, str) else b"\n"
|
||||||
|
|
||||||
return text.split(split_character)
|
return text.split(split_character)
|
||||||
|
|
||||||
|
@ -319,11 +319,11 @@ def getdraw(im=None, hints=None):
|
||||||
handler = None
|
handler = None
|
||||||
if not hints or "nicest" in hints:
|
if not hints or "nicest" in hints:
|
||||||
try:
|
try:
|
||||||
from PIL import _imagingagg as handler
|
from . import _imagingagg as handler
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
if handler is None:
|
if handler is None:
|
||||||
from PIL import ImageDraw2 as handler
|
from . import ImageDraw2 as handler
|
||||||
if im:
|
if im:
|
||||||
im = handler.Draw(im)
|
im = handler.Draw(im)
|
||||||
return im, handler
|
return im, handler
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image, ImageColor, ImageDraw, ImageFont, ImagePath
|
from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath
|
||||||
|
|
||||||
|
|
||||||
class Pen(object):
|
class Pen(object):
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image, ImageFilter, ImageStat
|
from . import Image, ImageFilter, ImageStat
|
||||||
|
|
||||||
|
|
||||||
class _Enhance(object):
|
class _Enhance(object):
|
||||||
|
|
|
@ -27,8 +27,8 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image
|
from . import Image
|
||||||
from PIL._util import isPath
|
from ._util import isPath
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
@ -150,15 +150,16 @@ class ImageFile(Image.Image):
|
||||||
|
|
||||||
if use_mmap:
|
if use_mmap:
|
||||||
# try memory mapping
|
# try memory mapping
|
||||||
d, e, o, a = self.tile[0]
|
decoder_name, extents, offset, args = self.tile[0]
|
||||||
if d == "raw" and a[0] == self.mode and a[0] in Image._MAPMODES:
|
if decoder_name == "raw" and len(args) >= 3 and args[0] == self.mode \
|
||||||
|
and args[0] in Image._MAPMODES:
|
||||||
try:
|
try:
|
||||||
if hasattr(Image.core, "map"):
|
if hasattr(Image.core, "map"):
|
||||||
# use built-in mapper
|
# use built-in mapper WIN32 only
|
||||||
self.map = Image.core.map(self.filename)
|
self.map = Image.core.map(self.filename)
|
||||||
self.map.seek(o)
|
self.map.seek(offset)
|
||||||
self.im = self.map.readimage(
|
self.im = self.map.readimage(
|
||||||
self.mode, self.size, a[1], a[2]
|
self.mode, self.size, args[1], args[2]
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# use mmap, if possible
|
# use mmap, if possible
|
||||||
|
@ -167,7 +168,7 @@ class ImageFile(Image.Image):
|
||||||
size = os.path.getsize(self.filename)
|
size = os.path.getsize(self.filename)
|
||||||
self.map = mmap.mmap(fp.fileno(), size, access=mmap.ACCESS_READ)
|
self.map = mmap.mmap(fp.fileno(), size, access=mmap.ACCESS_READ)
|
||||||
self.im = Image.core.map_buffer(
|
self.im = Image.core.map_buffer(
|
||||||
self.map, self.size, d, e, o, a
|
self.map, self.size, decoder_name, extents, offset, args
|
||||||
)
|
)
|
||||||
readonly = 1
|
readonly = 1
|
||||||
# After trashing self.im, we might need to reload the palette data.
|
# After trashing self.im, we might need to reload the palette data.
|
||||||
|
@ -210,7 +211,7 @@ class ImageFile(Image.Image):
|
||||||
else:
|
else:
|
||||||
raise IOError("image file is truncated")
|
raise IOError("image file is truncated")
|
||||||
|
|
||||||
if not s and not decoder.handles_eof: # truncated jpeg
|
if not s: # truncated jpeg
|
||||||
self.tile = []
|
self.tile = []
|
||||||
|
|
||||||
# JpegDecode needs to clean things up here either way
|
# JpegDecode needs to clean things up here either way
|
||||||
|
@ -242,12 +243,6 @@ class ImageFile(Image.Image):
|
||||||
# still raised if decoder fails to return anything
|
# still raised if decoder fails to return anything
|
||||||
raise_ioerror(err_code)
|
raise_ioerror(err_code)
|
||||||
|
|
||||||
# post processing
|
|
||||||
if hasattr(self, "tile_post_rotate"):
|
|
||||||
# FIXME: This is a hack to handle rotated PCD's
|
|
||||||
self.im = self.im.rotate(self.tile_post_rotate)
|
|
||||||
self.size = self.im.size
|
|
||||||
|
|
||||||
self.load_end()
|
self.load_end()
|
||||||
|
|
||||||
return Image.Image.load(self)
|
return Image.Image.load(self)
|
||||||
|
|
|
@ -25,8 +25,8 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image
|
from . import Image
|
||||||
from PIL._util import isDirectory, isPath
|
from ._util import isDirectory, isPath
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ class _imagingft_not_installed(object):
|
||||||
raise ImportError("The _imagingft C module is not installed")
|
raise ImportError("The _imagingft C module is not installed")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from PIL import _imagingft as core
|
from . import _imagingft as core
|
||||||
except ImportError:
|
except ImportError:
|
||||||
core = _imagingft_not_installed()
|
core = _imagingft_not_installed()
|
||||||
|
|
||||||
|
@ -62,23 +62,22 @@ class ImageFont(object):
|
||||||
|
|
||||||
def _load_pilfont(self, filename):
|
def _load_pilfont(self, filename):
|
||||||
|
|
||||||
fp = open(filename, "rb")
|
with open(filename, "rb") as fp:
|
||||||
|
for ext in (".png", ".gif", ".pbm"):
|
||||||
for ext in (".png", ".gif", ".pbm"):
|
try:
|
||||||
try:
|
fullname = os.path.splitext(filename)[0] + ext
|
||||||
fullname = os.path.splitext(filename)[0] + ext
|
image = Image.open(fullname)
|
||||||
image = Image.open(fullname)
|
except:
|
||||||
except:
|
pass
|
||||||
pass
|
else:
|
||||||
|
if image and image.mode in ("1", "L"):
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
if image and image.mode in ("1", "L"):
|
raise IOError("cannot find glyph data file")
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise IOError("cannot find glyph data file")
|
|
||||||
|
|
||||||
self.file = fullname
|
self.file = fullname
|
||||||
|
|
||||||
return self._load_pilfont_data(fp, image)
|
return self._load_pilfont_data(fp, image)
|
||||||
|
|
||||||
def _load_pilfont_data(self, file, image):
|
def _load_pilfont_data(self, file, image):
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image
|
from . import Image
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
if sys.platform not in ["win32", "darwin"]:
|
if sys.platform not in ["win32", "darwin"]:
|
||||||
|
@ -41,7 +41,7 @@ def grab(bbox=None):
|
||||||
size, data = grabber()
|
size, data = grabber()
|
||||||
im = Image.frombytes(
|
im = Image.frombytes(
|
||||||
"RGB", size, data,
|
"RGB", size, data,
|
||||||
# RGB, 32-bit line padding, origo in lower left corner
|
# RGB, 32-bit line padding, origin lower left corner
|
||||||
"raw", "BGR", (size[0]*3 + 3) & -4, -1
|
"raw", "BGR", (size[0]*3 + 3) & -4, -1
|
||||||
)
|
)
|
||||||
if bbox:
|
if bbox:
|
||||||
|
@ -75,7 +75,7 @@ def grabclipboard():
|
||||||
debug = 0 # temporary interface
|
debug = 0 # temporary interface
|
||||||
data = Image.core.grabclipboard(debug)
|
data = Image.core.grabclipboard(debug)
|
||||||
if isinstance(data, bytes):
|
if isinstance(data, bytes):
|
||||||
from PIL import BmpImagePlugin
|
from . import BmpImagePlugin
|
||||||
import io
|
import io
|
||||||
return BmpImagePlugin.DibImageFile(io.BytesIO(data))
|
return BmpImagePlugin.DibImageFile(io.BytesIO(data))
|
||||||
return data
|
return data
|
||||||
|
|
|
@ -15,8 +15,7 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image
|
from . import Image, _imagingmath
|
||||||
from PIL import _imagingmath
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import builtins
|
import builtins
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
# mode descriptor cache
|
# mode descriptor cache
|
||||||
_modes = {}
|
_modes = None
|
||||||
|
|
||||||
|
|
||||||
class ModeDescriptor(object):
|
class ModeDescriptor(object):
|
||||||
|
@ -32,19 +32,24 @@ class ModeDescriptor(object):
|
||||||
|
|
||||||
def getmode(mode):
|
def getmode(mode):
|
||||||
"""Gets a mode descriptor for the given mode."""
|
"""Gets a mode descriptor for the given mode."""
|
||||||
|
global _modes
|
||||||
if not _modes:
|
if not _modes:
|
||||||
# initialize mode cache
|
# initialize mode cache
|
||||||
from PIL import Image
|
|
||||||
|
from . import Image
|
||||||
|
modes = {}
|
||||||
# core modes
|
# core modes
|
||||||
for m, (basemode, basetype, bands) in Image._MODEINFO.items():
|
for m, (basemode, basetype, bands) in Image._MODEINFO.items():
|
||||||
_modes[m] = ModeDescriptor(m, bands, basemode, basetype)
|
modes[m] = ModeDescriptor(m, bands, basemode, basetype)
|
||||||
# extra experimental modes
|
# extra experimental modes
|
||||||
_modes["RGBa"] = ModeDescriptor("RGBa", ("R", "G", "B", "a"), "RGB", "L")
|
modes["RGBa"] = ModeDescriptor("RGBa", ("R", "G", "B", "a"), "RGB", "L")
|
||||||
_modes["LA"] = ModeDescriptor("LA", ("L", "A"), "L", "L")
|
modes["LA"] = ModeDescriptor("LA", ("L", "A"), "L", "L")
|
||||||
_modes["La"] = ModeDescriptor("La", ("L", "a"), "L", "L")
|
modes["La"] = ModeDescriptor("La", ("L", "a"), "L", "L")
|
||||||
_modes["PA"] = ModeDescriptor("PA", ("P", "A"), "RGB", "L")
|
modes["PA"] = ModeDescriptor("PA", ("P", "A"), "RGB", "L")
|
||||||
# mapping modes
|
# mapping modes
|
||||||
_modes["I;16"] = ModeDescriptor("I;16", "I", "L", "L")
|
modes["I;16"] = ModeDescriptor("I;16", "I", "L", "L")
|
||||||
_modes["I;16L"] = ModeDescriptor("I;16L", "I", "L", "L")
|
modes["I;16L"] = ModeDescriptor("I;16L", "I", "L", "L")
|
||||||
_modes["I;16B"] = ModeDescriptor("I;16B", "I", "L", "L")
|
modes["I;16B"] = ModeDescriptor("I;16B", "I", "L", "L")
|
||||||
|
# set global mode cache atomically
|
||||||
|
_modes = modes
|
||||||
return _modes[mode]
|
return _modes[mode]
|
||||||
|
|
|
@ -5,8 +5,9 @@
|
||||||
#
|
#
|
||||||
# Copyright (c) 2014 Dov Grobgeld <dov.grobgeld@gmail.com>
|
# Copyright (c) 2014 Dov Grobgeld <dov.grobgeld@gmail.com>
|
||||||
|
|
||||||
from PIL import Image
|
from __future__ import print_function
|
||||||
from PIL import _imagingmorph
|
|
||||||
|
from . import Image, _imagingmorph
|
||||||
import re
|
import re
|
||||||
|
|
||||||
LUT_SIZE = 1 << 9
|
LUT_SIZE = 1 << 9
|
||||||
|
@ -78,7 +79,7 @@ class LutBuilder(object):
|
||||||
def build_default_lut(self):
|
def build_default_lut(self):
|
||||||
symbols = [0, 1]
|
symbols = [0, 1]
|
||||||
m = 1 << 4 # pos of current pixel
|
m = 1 << 4 # pos of current pixel
|
||||||
self.lut = bytearray([symbols[(i & m) > 0] for i in range(LUT_SIZE)])
|
self.lut = bytearray(symbols[(i & m) > 0] for i in range(LUT_SIZE))
|
||||||
|
|
||||||
def get_lut(self):
|
def get_lut(self):
|
||||||
return self.lut
|
return self.lut
|
||||||
|
@ -88,7 +89,7 @@ class LutBuilder(object):
|
||||||
string permuted according to the permutation list.
|
string permuted according to the permutation list.
|
||||||
"""
|
"""
|
||||||
assert(len(permutation) == 9)
|
assert(len(permutation) == 9)
|
||||||
return ''.join([pattern[p] for p in permutation])
|
return ''.join(pattern[p] for p in permutation)
|
||||||
|
|
||||||
def _pattern_permute(self, basic_pattern, options, basic_result):
|
def _pattern_permute(self, basic_pattern, options, basic_result):
|
||||||
"""pattern_permute takes a basic pattern and its result and clones
|
"""pattern_permute takes a basic pattern and its result and clones
|
||||||
|
@ -152,14 +153,14 @@ class LutBuilder(object):
|
||||||
|
|
||||||
# # Debugging
|
# # Debugging
|
||||||
# for p,r in patterns:
|
# for p,r in patterns:
|
||||||
# print p,r
|
# print(p,r)
|
||||||
# print '--'
|
# print('--')
|
||||||
|
|
||||||
# compile the patterns into regular expressions for speed
|
# compile the patterns into regular expressions for speed
|
||||||
for i in range(len(patterns)):
|
for i, pattern in enumerate(patterns):
|
||||||
p = patterns[i][0].replace('.', 'X').replace('X', '[01]')
|
p = pattern[0].replace('.', 'X').replace('X', '[01]')
|
||||||
p = re.compile(p)
|
p = re.compile(p)
|
||||||
patterns[i] = (p, patterns[i][1])
|
patterns[i] = (p, pattern[1])
|
||||||
|
|
||||||
# Step through table and find patterns that match.
|
# Step through table and find patterns that match.
|
||||||
# Note that all the patterns are searched. The last one
|
# Note that all the patterns are searched. The last one
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image
|
from . import Image
|
||||||
from PIL._util import isStringType
|
from ._util import isStringType
|
||||||
import operator
|
import operator
|
||||||
import functools
|
import functools
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ def _border(border):
|
||||||
|
|
||||||
def _color(color, mode):
|
def _color(color, mode):
|
||||||
if isStringType(color):
|
if isStringType(color):
|
||||||
from PIL import ImageColor
|
from . import ImageColor
|
||||||
color = ImageColor.getcolor(color, mode)
|
color = ImageColor.getcolor(color, mode)
|
||||||
return color
|
return color
|
||||||
|
|
||||||
|
@ -206,7 +206,8 @@ def deform(image, deformer, resample=Image.BILINEAR):
|
||||||
:param image: The image to deform.
|
:param image: The image to deform.
|
||||||
:param deformer: A deformer object. Any object that implements a
|
:param deformer: A deformer object. Any object that implements a
|
||||||
**getmesh** method can be used.
|
**getmesh** method can be used.
|
||||||
:param resample: What resampling filter to use.
|
:param resample: An optional resampling filter. Same values possible as
|
||||||
|
in the PIL.Image.transform function.
|
||||||
:return: An image.
|
:return: An image.
|
||||||
"""
|
"""
|
||||||
return image.transform(
|
return image.transform(
|
||||||
|
|
|
@ -17,10 +17,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
import array
|
import array
|
||||||
from PIL import ImageColor
|
from . import ImageColor, GimpPaletteFile, GimpGradientFile, PaletteFile
|
||||||
from PIL import GimpPaletteFile
|
|
||||||
from PIL import GimpGradientFile
|
|
||||||
from PIL import PaletteFile
|
|
||||||
|
|
||||||
|
|
||||||
class ImagePalette(object):
|
class ImagePalette(object):
|
||||||
|
@ -197,23 +194,23 @@ def load(filename):
|
||||||
|
|
||||||
# FIXME: supports GIMP gradients only
|
# FIXME: supports GIMP gradients only
|
||||||
|
|
||||||
fp = open(filename, "rb")
|
with open(filename, "rb") as fp:
|
||||||
|
|
||||||
for paletteHandler in [
|
for paletteHandler in [
|
||||||
GimpPaletteFile.GimpPaletteFile,
|
GimpPaletteFile.GimpPaletteFile,
|
||||||
GimpGradientFile.GimpGradientFile,
|
GimpGradientFile.GimpGradientFile,
|
||||||
PaletteFile.PaletteFile
|
PaletteFile.PaletteFile
|
||||||
]:
|
]:
|
||||||
try:
|
try:
|
||||||
fp.seek(0)
|
fp.seek(0)
|
||||||
lut = paletteHandler(fp).getpalette()
|
lut = paletteHandler(fp).getpalette()
|
||||||
if lut:
|
if lut:
|
||||||
break
|
break
|
||||||
except (SyntaxError, ValueError):
|
except (SyntaxError, ValueError):
|
||||||
# import traceback
|
# import traceback
|
||||||
# traceback.print_exc()
|
# traceback.print_exc()
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
raise IOError("cannot load palette")
|
raise IOError("cannot load palette")
|
||||||
|
|
||||||
return lut # data, rawmode
|
return lut # data, rawmode
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image
|
from . import Image
|
||||||
|
|
||||||
|
|
||||||
# the Python class below is overridden by the C implementation.
|
# the Python class below is overridden by the C implementation.
|
||||||
|
|
|
@ -16,8 +16,8 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image
|
from . import Image
|
||||||
from PIL._util import isPath
|
from ._util import isPath
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
qt_is_installed = True
|
qt_is_installed = True
|
||||||
|
@ -157,7 +157,6 @@ def _toqclass_helper(im):
|
||||||
else:
|
else:
|
||||||
raise ValueError("unsupported image mode %r" % im.mode)
|
raise ValueError("unsupported image mode %r" % im.mode)
|
||||||
|
|
||||||
# must keep a reference, or Qt will crash!
|
|
||||||
__data = data or align8to32(im.tobytes(), im.size[0], im.mode)
|
__data = data or align8to32(im.tobytes(), im.size[0], im.mode)
|
||||||
return {
|
return {
|
||||||
'data': __data, 'im': im, 'format': format, 'colortable': colortable
|
'data': __data, 'im': im, 'format': format, 'colortable': colortable
|
||||||
|
@ -175,8 +174,13 @@ if qt_is_installed:
|
||||||
string or a PyQt string object).
|
string or a PyQt string object).
|
||||||
"""
|
"""
|
||||||
im_data = _toqclass_helper(im)
|
im_data = _toqclass_helper(im)
|
||||||
|
# must keep a reference, or Qt will crash!
|
||||||
|
# All QImage constructors that take data operate on an existing
|
||||||
|
# buffer, so this buffer has to hang on for the life of the image.
|
||||||
|
# Fixes https://github.com/python-pillow/Pillow/issues/1370
|
||||||
|
self.__data = im_data['data']
|
||||||
QImage.__init__(self,
|
QImage.__init__(self,
|
||||||
im_data['data'], im_data['im'].size[0],
|
self.__data, im_data['im'].size[0],
|
||||||
im_data['im'].size[1], im_data['format'])
|
im_data['im'].size[1], im_data['format'])
|
||||||
if im_data['colortable']:
|
if im_data['colortable']:
|
||||||
self.setColorTable(im_data['colortable'])
|
self.setColorTable(im_data['colortable'])
|
||||||
|
|
|
@ -32,7 +32,14 @@ except ImportError:
|
||||||
tkinter = Tkinter
|
tkinter = Tkinter
|
||||||
del Tkinter
|
del Tkinter
|
||||||
|
|
||||||
from PIL import Image
|
# required for pypy, which always has cffi installed
|
||||||
|
try:
|
||||||
|
from cffi import FFI
|
||||||
|
ffi = FFI()
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
from . import Image
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
|
|
||||||
|
@ -182,9 +189,15 @@ class PhotoImage(object):
|
||||||
except tkinter.TclError:
|
except tkinter.TclError:
|
||||||
# activate Tkinter hook
|
# activate Tkinter hook
|
||||||
try:
|
try:
|
||||||
from PIL import _imagingtk
|
from . import _imagingtk
|
||||||
try:
|
try:
|
||||||
_imagingtk.tkinit(tk.interpaddr(), 1)
|
if hasattr(tk, 'interp'):
|
||||||
|
# Pypy is using a ffi cdata element
|
||||||
|
# (Pdb) self.tk.interp
|
||||||
|
# <cdata 'Tcl_Interp *' 0x3061b50>
|
||||||
|
_imagingtk.tkinit(int(ffi.cast("uintptr_t", tk.interp)), 1)
|
||||||
|
else:
|
||||||
|
_imagingtk.tkinit(tk.interpaddr(), 1)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
_imagingtk.tkinit(id(tk), 0)
|
_imagingtk.tkinit(id(tk), 0)
|
||||||
tk.call("PyImagingPhoto", self.__photo, block.id)
|
tk.call("PyImagingPhoto", self.__photo, block.id)
|
||||||
|
@ -264,6 +277,8 @@ class BitmapImage(object):
|
||||||
|
|
||||||
|
|
||||||
def getimage(photo):
|
def getimage(photo):
|
||||||
|
""" This function is unimplemented """
|
||||||
|
|
||||||
"""Copies the contents of a PhotoImage to a PIL image memory."""
|
"""Copies the contents of a PhotoImage to a PIL image memory."""
|
||||||
photo.tk.call("PyImagingPhotoGet", photo)
|
photo.tk.call("PyImagingPhotoGet", photo)
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image
|
from . import Image
|
||||||
|
|
||||||
|
|
||||||
class Transform(Image.ImageTransformHandler):
|
class Transform(Image.ImageTransformHandler):
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image
|
from . import Image
|
||||||
|
|
||||||
|
|
||||||
class HDC(object):
|
class HDC(object):
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from PIL import Image, ImageFile
|
from . import Image, ImageFile
|
||||||
|
|
||||||
__version__ = "0.2"
|
__version__ = "0.2"
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ class ImtImageFile(ImageFile.ImageFile):
|
||||||
s = s + self.fp.readline()
|
s = s + self.fp.readline()
|
||||||
if len(s) == 1 or len(s) > 100:
|
if len(s) == 1 or len(s) > 100:
|
||||||
break
|
break
|
||||||
if s[0] == b"*":
|
if s[0] == ord(b"*"):
|
||||||
continue # comment
|
continue # comment
|
||||||
|
|
||||||
m = field.match(s)
|
m = field.match(s)
|
||||||
|
|
|
@ -17,17 +17,13 @@
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
from PIL import Image, ImageFile, _binary
|
from . import Image, ImageFile
|
||||||
|
from ._binary import i8, i16be as i16, i32be as i32, o8
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
__version__ = "0.3"
|
__version__ = "0.3"
|
||||||
|
|
||||||
i8 = _binary.i8
|
|
||||||
i16 = _binary.i16be
|
|
||||||
i32 = _binary.i32be
|
|
||||||
o8 = _binary.o8
|
|
||||||
|
|
||||||
COMPRESSION = {
|
COMPRESSION = {
|
||||||
1: "raw",
|
1: "raw",
|
||||||
5: "jpeg"
|
5: "jpeg"
|
||||||
|
@ -107,7 +103,7 @@ class IptcImageFile(ImageFile.ImageFile):
|
||||||
else:
|
else:
|
||||||
self.info[tag] = tagdata
|
self.info[tag] = tagdata
|
||||||
|
|
||||||
# print tag, self.info[tag]
|
# print(tag, self.info[tag])
|
||||||
|
|
||||||
# mode
|
# mode
|
||||||
layers = i8(self.info[(3, 60)][0])
|
layers = i8(self.info[(3, 60)][0])
|
||||||
|
@ -168,14 +164,9 @@ class IptcImageFile(ImageFile.ImageFile):
|
||||||
o.close()
|
o.close()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
try:
|
_im = Image.open(outfile)
|
||||||
# fast
|
_im.load()
|
||||||
self.im = Image.core.open_ppm(outfile)
|
self.im = _im.im
|
||||||
except:
|
|
||||||
# slightly slower
|
|
||||||
im = Image.open(outfile)
|
|
||||||
im.load()
|
|
||||||
self.im = im.im
|
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
os.unlink(outfile)
|
os.unlink(outfile)
|
||||||
|
@ -196,7 +187,7 @@ def getiptcinfo(im):
|
||||||
:returns: A dictionary containing IPTC information, or None if
|
:returns: A dictionary containing IPTC information, or None if
|
||||||
no IPTC information block was found.
|
no IPTC information block was found.
|
||||||
"""
|
"""
|
||||||
from PIL import TiffImagePlugin, JpegImagePlugin
|
from . import TiffImagePlugin, JpegImagePlugin
|
||||||
import io
|
import io
|
||||||
|
|
||||||
data = None
|
data = None
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#
|
#
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
from PIL import Image, ImageFile
|
from . import Image, ImageFile
|
||||||
import struct
|
import struct
|
||||||
import os
|
import os
|
||||||
import io
|
import io
|
||||||
|
|
|
@ -32,19 +32,16 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
import array
|
import array
|
||||||
import struct
|
import struct
|
||||||
import io
|
import io
|
||||||
import warnings
|
import warnings
|
||||||
from struct import unpack_from
|
from . import Image, ImageFile, TiffImagePlugin
|
||||||
from PIL import Image, ImageFile, TiffImagePlugin, _binary
|
from ._binary import i8, o8, i16be as i16, i32be as i32
|
||||||
from PIL.JpegPresets import presets
|
from .JpegPresets import presets
|
||||||
from PIL._util import isStringType
|
from ._util import isStringType
|
||||||
|
|
||||||
i8 = _binary.i8
|
|
||||||
o8 = _binary.o8
|
|
||||||
i16 = _binary.i16be
|
|
||||||
i32 = _binary.i32be
|
|
||||||
|
|
||||||
__version__ = "0.6"
|
__version__ = "0.6"
|
||||||
|
|
||||||
|
@ -316,7 +313,7 @@ class JpegImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
if i in MARKER:
|
if i in MARKER:
|
||||||
name, description, handler = MARKER[i]
|
name, description, handler = MARKER[i]
|
||||||
# print hex(i), name, description
|
# print(hex(i), name, description)
|
||||||
if handler is not None:
|
if handler is not None:
|
||||||
handler(self, i)
|
handler(self, i)
|
||||||
if i == 0xFFDA: # start of scan
|
if i == 0xFFDA: # start of scan
|
||||||
|
@ -341,6 +338,10 @@ class JpegImageFile(ImageFile.ImageFile):
|
||||||
if len(self.tile) != 1:
|
if len(self.tile) != 1:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Protect from second call
|
||||||
|
if self.decoderconfig:
|
||||||
|
return
|
||||||
|
|
||||||
d, e, o, a = self.tile[0]
|
d, e, o, a = self.tile[0]
|
||||||
scale = 0
|
scale = 0
|
||||||
|
|
||||||
|
@ -349,7 +350,7 @@ class JpegImageFile(ImageFile.ImageFile):
|
||||||
a = mode, ""
|
a = mode, ""
|
||||||
|
|
||||||
if size:
|
if size:
|
||||||
scale = max(self.size[0] // size[0], self.size[1] // size[1])
|
scale = min(self.size[0] // size[0], self.size[1] // size[1])
|
||||||
for s in [8, 4, 2, 1]:
|
for s in [8, 4, 2, 1]:
|
||||||
if scale >= s:
|
if scale >= s:
|
||||||
break
|
break
|
||||||
|
@ -377,7 +378,9 @@ class JpegImageFile(ImageFile.ImageFile):
|
||||||
raise ValueError("Invalid Filename")
|
raise ValueError("Invalid Filename")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.im = Image.core.open_ppm(path)
|
_im = Image.open(path)
|
||||||
|
_im.load()
|
||||||
|
self.im = _im.im
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
|
@ -406,7 +409,7 @@ def _fixup_dict(src_dict):
|
||||||
except: pass
|
except: pass
|
||||||
return value
|
return value
|
||||||
|
|
||||||
return dict([(k, _fixup(v)) for k, v in src_dict.items()])
|
return {k: _fixup(v) for k, v in src_dict.items()}
|
||||||
|
|
||||||
|
|
||||||
def _getexif(self):
|
def _getexif(self):
|
||||||
|
@ -485,8 +488,8 @@ def _getmp(self):
|
||||||
try:
|
try:
|
||||||
rawmpentries = mp[0xB002]
|
rawmpentries = mp[0xB002]
|
||||||
for entrynum in range(0, quant):
|
for entrynum in range(0, quant):
|
||||||
unpackedentry = unpack_from(
|
unpackedentry = struct.unpack_from(
|
||||||
'{0}LLLHH'.format(endianness), rawmpentries, entrynum * 16)
|
'{}LLLHH'.format(endianness), rawmpentries, entrynum * 16)
|
||||||
labels = ('Attribute', 'Size', 'DataOffset', 'EntryNo1',
|
labels = ('Attribute', 'Size', 'DataOffset', 'EntryNo1',
|
||||||
'EntryNo2')
|
'EntryNo2')
|
||||||
mpentry = dict(zip(labels, unpackedentry))
|
mpentry = dict(zip(labels, unpackedentry))
|
||||||
|
@ -710,8 +713,11 @@ def _save(im, fp, filename):
|
||||||
# https://github.com/matthewwithanm/django-imagekit/issues/50
|
# https://github.com/matthewwithanm/django-imagekit/issues/50
|
||||||
bufsize = 0
|
bufsize = 0
|
||||||
if optimize or progressive:
|
if optimize or progressive:
|
||||||
|
# CMYK can be bigger
|
||||||
|
if im.mode == 'CMYK':
|
||||||
|
bufsize = 4 * im.size[0] * im.size[1]
|
||||||
# keep sets quality to 0, but the actual value may be high.
|
# keep sets quality to 0, but the actual value may be high.
|
||||||
if quality >= 95 or quality == 0:
|
elif quality >= 95 or quality == 0:
|
||||||
bufsize = 2 * im.size[0] * im.size[1]
|
bufsize = 2 * im.size[0] * im.size[1]
|
||||||
else:
|
else:
|
||||||
bufsize = im.size[0] * im.size[1]
|
bufsize = im.size[0] * im.size[1]
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
import struct
|
import struct
|
||||||
from PIL import Image, ImageFile
|
from . import Image, ImageFile
|
||||||
|
|
||||||
__version__ = "0.2"
|
__version__ = "0.2"
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,9 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
from PIL import Image, TiffImagePlugin
|
from . import Image, TiffImagePlugin
|
||||||
from PIL.OleFileIO import MAGIC, OleFileIO
|
|
||||||
|
import olefile
|
||||||
|
|
||||||
__version__ = "0.1"
|
__version__ = "0.1"
|
||||||
|
|
||||||
|
@ -28,7 +29,7 @@ __version__ = "0.1"
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix):
|
def _accept(prefix):
|
||||||
return prefix[:8] == MAGIC
|
return prefix[:8] == olefile.MAGIC
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -45,7 +46,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
|
||||||
# to be a Microsoft Image Composer file
|
# to be a Microsoft Image Composer file
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.ole = OleFileIO(self.fp)
|
self.ole = olefile.OleFileIO(self.fp)
|
||||||
except IOError:
|
except IOError:
|
||||||
raise SyntaxError("not an MIC file; invalid OLE file")
|
raise SyntaxError("not an MIC file; invalid OLE file")
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,8 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
from PIL import Image, ImageFile
|
from . import Image, ImageFile
|
||||||
from PIL._binary import i8
|
from ._binary import i8
|
||||||
|
|
||||||
__version__ = "0.1"
|
__version__ = "0.1"
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image, JpegImagePlugin
|
from . import Image, JpegImagePlugin
|
||||||
|
|
||||||
__version__ = "0.1"
|
__version__ = "0.1"
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,8 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
from PIL import Image, ImageFile, _binary
|
from . import Image, ImageFile
|
||||||
|
from ._binary import i16le as i16, o16le as o16
|
||||||
|
|
||||||
__version__ = "0.1"
|
__version__ = "0.1"
|
||||||
|
|
||||||
|
@ -25,8 +26,6 @@ __version__ = "0.1"
|
||||||
#
|
#
|
||||||
# read MSP files
|
# read MSP files
|
||||||
|
|
||||||
i16 = _binary.i16le
|
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix):
|
def _accept(prefix):
|
||||||
return prefix[:4] in [b"DanM", b"LinS"]
|
return prefix[:4] in [b"DanM", b"LinS"]
|
||||||
|
@ -66,8 +65,6 @@ class MspImageFile(ImageFile.ImageFile):
|
||||||
#
|
#
|
||||||
# write MSP files (uncompressed only)
|
# write MSP files (uncompressed only)
|
||||||
|
|
||||||
o16 = _binary.o16le
|
|
||||||
|
|
||||||
|
|
||||||
def _save(im, fp, filename):
|
def _save(im, fp, filename):
|
||||||
|
|
||||||
|
|
|
@ -1,180 +0,0 @@
|
||||||
olefile (formerly OleFileIO_PL)
|
|
||||||
===============================
|
|
||||||
|
|
||||||
[olefile](http://www.decalage.info/olefile) is a Python package to parse, read and write
|
|
||||||
[Microsoft OLE2 files](http://en.wikipedia.org/wiki/Compound_File_Binary_Format)
|
|
||||||
(also called Structured Storage, Compound File Binary Format or Compound Document File Format),
|
|
||||||
such as Microsoft Office 97-2003 documents, vbaProject.bin in MS Office 2007+ files, Image Composer
|
|
||||||
and FlashPix files, Outlook messages, StickyNotes, several Microscopy file formats, McAfee antivirus quarantine files,
|
|
||||||
etc.
|
|
||||||
|
|
||||||
|
|
||||||
**Quick links:** [Home page](http://www.decalage.info/olefile) -
|
|
||||||
[Download/Install](https://bitbucket.org/decalage/olefileio_pl/wiki/Install) -
|
|
||||||
[Documentation](https://bitbucket.org/decalage/olefileio_pl/wiki) -
|
|
||||||
[Report Issues/Suggestions/Questions](https://bitbucket.org/decalage/olefileio_pl/issues?status=new&status=open) -
|
|
||||||
[Contact the author](http://decalage.info/contact) -
|
|
||||||
[Repository](https://bitbucket.org/decalage/olefileio_pl) -
|
|
||||||
[Updates on Twitter](https://twitter.com/decalage2)
|
|
||||||
|
|
||||||
|
|
||||||
News
|
|
||||||
----
|
|
||||||
|
|
||||||
Follow all updates and news on Twitter: <https://twitter.com/decalage2>
|
|
||||||
|
|
||||||
- **2015-01-25 v0.42**: improved handling of special characters in stream/storage names on Python 2.x (using UTF-8
|
|
||||||
instead of Latin-1), fixed bug in listdir with empty storages.
|
|
||||||
- 2014-11-25 v0.41: OleFileIO.open and isOleFile now support OLE files stored in byte strings, fixed installer for
|
|
||||||
python 3, added support for Jython (Niko Ehrenfeuchter)
|
|
||||||
- 2014-10-01 v0.40: renamed OleFileIO_PL to olefile, added initial write support for streams >4K, updated doc and
|
|
||||||
license, improved the setup script.
|
|
||||||
- 2014-07-27 v0.31: fixed support for large files with 4K sectors, thanks to Niko Ehrenfeuchter, Martijn Berger and
|
|
||||||
Dave Jones. Added test scripts from Pillow (by hugovk). Fixed setup for Python 3 (Martin Panter)
|
|
||||||
- 2014-02-04 v0.30: now compatible with Python 3.x, thanks to Martin Panter who did most of the hard work.
|
|
||||||
- 2013-07-24 v0.26: added methods to parse stream/storage timestamps, improved listdir to include storages, fixed
|
|
||||||
parsing of direntry timestamps
|
|
||||||
- 2013-05-27 v0.25: improved metadata extraction, properties parsing and exception handling, fixed
|
|
||||||
[issue #12](https://bitbucket.org/decalage/olefileio_pl/issue/12/error-when-converting-timestamps-in-ole)
|
|
||||||
- 2013-05-07 v0.24: new features to extract metadata (get\_metadata method and OleMetadata class), improved
|
|
||||||
getproperties to convert timestamps to Python datetime
|
|
||||||
- 2012-10-09: published [python-oletools](http://www.decalage.info/python/oletools), a package of analysis tools based
|
|
||||||
on OleFileIO_PL
|
|
||||||
- 2012-09-11 v0.23: added support for file-like objects, fixed [issue #8](https://bitbucket.org/decalage/olefileio_pl/issue/8/bug-with-file-object)
|
|
||||||
- 2012-02-17 v0.22: fixed issues #7 (bug in getproperties) and #2 (added close method)
|
|
||||||
- 2011-10-20: code hosted on bitbucket to ease contributions and bug tracking
|
|
||||||
- 2010-01-24 v0.21: fixed support for big-endian CPUs, such as PowerPC Macs.
|
|
||||||
- 2009-12-11 v0.20: small bugfix in OleFileIO.open when filename is not plain str.
|
|
||||||
- 2009-12-10 v0.19: fixed support for 64 bits platforms (thanks to Ben G. and Martijn for reporting the bug)
|
|
||||||
- see changelog in source code for more info.
|
|
||||||
|
|
||||||
Download/Install
|
|
||||||
----------------
|
|
||||||
|
|
||||||
If you have pip or setuptools installed (pip is included in Python 2.7.9+), you may simply run **pip install olefile**
|
|
||||||
or **easy_install olefile** for the first installation.
|
|
||||||
|
|
||||||
To update olefile, run **pip install -U olefile**.
|
|
||||||
|
|
||||||
Otherwise, see https://bitbucket.org/decalage/olefileio_pl/wiki/Install
|
|
||||||
|
|
||||||
Features
|
|
||||||
--------
|
|
||||||
|
|
||||||
- Parse, read and write any OLE file such as Microsoft Office 97-2003 legacy document formats (Word .doc, Excel .xls,
|
|
||||||
PowerPoint .ppt, Visio .vsd, Project .mpp), Image Composer and FlashPix files, Outlook messages, StickyNotes,
|
|
||||||
Zeiss AxioVision ZVI files, Olympus FluoView OIB files, etc
|
|
||||||
- List all the streams and storages contained in an OLE file
|
|
||||||
- Open streams as files
|
|
||||||
- Parse and read property streams, containing metadata of the file
|
|
||||||
- Portable, pure Python module, no dependency
|
|
||||||
|
|
||||||
olefile can be used as an independent package or with PIL/Pillow.
|
|
||||||
|
|
||||||
olefile is mostly meant for developers. If you are looking for tools to analyze OLE files or to extract data (especially
|
|
||||||
for security purposes such as malware analysis and forensics), then please also check my
|
|
||||||
[python-oletools](http://www.decalage.info/python/oletools), which are built upon olefile and provide a higher-level interface.
|
|
||||||
|
|
||||||
|
|
||||||
History
|
|
||||||
-------
|
|
||||||
|
|
||||||
olefile is based on the OleFileIO module from [PIL](http://www.pythonware.com/products/pil/index.htm), the excellent
|
|
||||||
Python Imaging Library, created and maintained by Fredrik Lundh. The olefile API is still compatible with PIL, but
|
|
||||||
since 2005 I have improved the internal implementation significantly, with new features, bugfixes and a more robust
|
|
||||||
design. From 2005 to 2014 the project was called OleFileIO_PL, and in 2014 I changed its name to olefile to celebrate
|
|
||||||
its 9 years and its new write features.
|
|
||||||
|
|
||||||
As far as I know, olefile is the most complete and robust Python implementation to read MS OLE2 files, portable on
|
|
||||||
several operating systems. (please tell me if you know other similar Python modules)
|
|
||||||
|
|
||||||
Since 2014 olefile/OleFileIO_PL has been integrated into [Pillow](http://python-pillow.org), the friendly fork
|
|
||||||
of PIL. olefile will continue to be improved as a separate project, and new versions will be merged into Pillow
|
|
||||||
regularly.
|
|
||||||
|
|
||||||
|
|
||||||
Main improvements over the original version of OleFileIO in PIL:
|
|
||||||
----------------------------------------------------------------
|
|
||||||
|
|
||||||
- Compatible with Python 3.x and 2.6+
|
|
||||||
- Many bug fixes
|
|
||||||
- Support for files larger than 6.8MB
|
|
||||||
- Support for 64 bits platforms and big-endian CPUs
|
|
||||||
- Robust: many checks to detect malformed files
|
|
||||||
- Runtime option to choose if malformed files should be parsed or raise exceptions
|
|
||||||
- Improved API
|
|
||||||
- Metadata extraction, stream/storage timestamps (e.g. for document forensics)
|
|
||||||
- Can open file-like objects
|
|
||||||
- Added setup.py and install.bat to ease installation
|
|
||||||
- More convenient slash-based syntax for stream paths
|
|
||||||
- Write features
|
|
||||||
|
|
||||||
Documentation
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Please see the [online documentation](https://bitbucket.org/decalage/olefileio_pl/wiki) for more information,
|
|
||||||
especially the [OLE overview](https://bitbucket.org/decalage/olefileio_pl/wiki/OLE_Overview) and the
|
|
||||||
[API page](https://bitbucket.org/decalage/olefileio_pl/wiki/API) which describe how to use olefile in Python applications.
|
|
||||||
A copy of the same documentation is also provided in the doc subfolder of the olefile package.
|
|
||||||
|
|
||||||
|
|
||||||
## Real-life examples ##
|
|
||||||
|
|
||||||
A real-life example: [using OleFileIO_PL for malware analysis and forensics](http://blog.gregback.net/2011/03/using-remnux-for-forensic-puzzle-6/).
|
|
||||||
|
|
||||||
See also [this paper](https://computer-forensics.sans.org/community/papers/gcfa/grow-forensic-tools-taxonomy-python-libraries-helpful-forensic-analysis_6879) about python tools for forensics, which features olefile.
|
|
||||||
|
|
||||||
|
|
||||||
License
|
|
||||||
-------
|
|
||||||
|
|
||||||
olefile (formerly OleFileIO_PL) is copyright (c) 2005-2015 Philippe Lagadec
|
|
||||||
([http://www.decalage.info](http://www.decalage.info))
|
|
||||||
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
|
|
||||||
----------
|
|
||||||
|
|
||||||
olefile is based on source code from the OleFileIO module of the Python Imaging Library (PIL) published by Fredrik
|
|
||||||
Lundh under the following license:
|
|
||||||
|
|
||||||
The Python Imaging Library (PIL) is
|
|
||||||
|
|
||||||
Copyright © 1997-2011 by Secret Labs AB
|
|
||||||
Copyright © 1995-2011 by Fredrik Lundh
|
|
||||||
|
|
||||||
By obtaining, using, and/or copying this software and/or its associated documentation, you agree that you have read,
|
|
||||||
understood, and will comply with the following terms and conditions:
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and distribute this software and its associated documentation for any purpose and
|
|
||||||
without fee is hereby granted, provided that the above copyright notice appears in all copies, and that both that
|
|
||||||
copyright notice and this permission notice appear in supporting documentation, and that the name of Secret Labs AB or
|
|
||||||
the author not be used in advertising or publicity pertaining to distribution of the software without specific, written
|
|
||||||
prior permission.
|
|
||||||
|
|
||||||
SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
|
|
||||||
OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
|
||||||
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
|
||||||
CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
|
||||||
SOFTWARE.
|
|
2309
PIL/OleFileIO.py
Executable file → Normal file
2309
PIL/OleFileIO.py
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
|
@ -15,7 +15,7 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import EpsImagePlugin
|
from . import EpsImagePlugin
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL._binary import o8
|
from ._binary import o8
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
# Image plugin for Palm pixmap images (output only).
|
# Image plugin for Palm pixmap images (output only).
|
||||||
##
|
##
|
||||||
|
|
||||||
from PIL import Image, ImageFile, _binary
|
from . import Image, ImageFile
|
||||||
|
from ._binary import o8, o16be as o16b
|
||||||
|
|
||||||
__version__ = "1.0"
|
__version__ = "1.0"
|
||||||
|
|
||||||
|
@ -80,13 +81,12 @@ _Palm8BitColormapValues = (
|
||||||
|
|
||||||
# so build a prototype image to be used for palette resampling
|
# so build a prototype image to be used for palette resampling
|
||||||
def build_prototype_image():
|
def build_prototype_image():
|
||||||
image = Image.new("L", (1, len(_Palm8BitColormapValues),))
|
image = Image.new("L", (1, len(_Palm8BitColormapValues)))
|
||||||
image.putdata(list(range(len(_Palm8BitColormapValues))))
|
image.putdata(list(range(len(_Palm8BitColormapValues))))
|
||||||
palettedata = ()
|
palettedata = ()
|
||||||
for i in range(len(_Palm8BitColormapValues)):
|
for colormapValue in _Palm8BitColormapValues:
|
||||||
palettedata = palettedata + _Palm8BitColormapValues[i]
|
palettedata += colormapValue
|
||||||
for i in range(256 - len(_Palm8BitColormapValues)):
|
palettedata += (0, 0, 0)*(256 - len(_Palm8BitColormapValues))
|
||||||
palettedata = palettedata + (0, 0, 0)
|
|
||||||
image.putpalette(palettedata)
|
image.putpalette(palettedata)
|
||||||
return image
|
return image
|
||||||
|
|
||||||
|
@ -109,9 +109,6 @@ _COMPRESSION_TYPES = {
|
||||||
"scanline": 0x00,
|
"scanline": 0x00,
|
||||||
}
|
}
|
||||||
|
|
||||||
o8 = _binary.o8
|
|
||||||
o16b = _binary.o16be
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
|
|
|
@ -15,12 +15,11 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
from PIL import Image, ImageFile, _binary
|
from . import Image, ImageFile
|
||||||
|
from ._binary import i8
|
||||||
|
|
||||||
__version__ = "0.1"
|
__version__ = "0.1"
|
||||||
|
|
||||||
i8 = _binary.i8
|
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Image plugin for PhotoCD images. This plugin only reads the 768x512
|
# Image plugin for PhotoCD images. This plugin only reads the 768x512
|
||||||
|
@ -42,8 +41,9 @@ class PcdImageFile(ImageFile.ImageFile):
|
||||||
raise SyntaxError("not a PCD file")
|
raise SyntaxError("not a PCD file")
|
||||||
|
|
||||||
orientation = i8(s[1538]) & 3
|
orientation = i8(s[1538]) & 3
|
||||||
|
self.tile_post_rotate = None
|
||||||
if orientation == 1:
|
if orientation == 1:
|
||||||
self.tile_post_rotate = 90 # hack
|
self.tile_post_rotate = 90
|
||||||
elif orientation == 3:
|
elif orientation == 3:
|
||||||
self.tile_post_rotate = -90
|
self.tile_post_rotate = -90
|
||||||
|
|
||||||
|
@ -51,6 +51,13 @@ class PcdImageFile(ImageFile.ImageFile):
|
||||||
self.size = 768, 512 # FIXME: not correct for rotated images!
|
self.size = 768, 512 # FIXME: not correct for rotated images!
|
||||||
self.tile = [("pcd", (0, 0)+self.size, 96*2048, None)]
|
self.tile = [("pcd", (0, 0)+self.size, 96*2048, None)]
|
||||||
|
|
||||||
|
def load_end(self):
|
||||||
|
if self.tile_post_rotate:
|
||||||
|
# Handle rotated PCDs
|
||||||
|
self.im = self.im.rotate(self.tile_post_rotate)
|
||||||
|
self.size = self.im.size
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# registry
|
# registry
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,8 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image
|
from . import Image, FontFile
|
||||||
from PIL import FontFile
|
from ._binary import i8, i16le as l16, i32le as l32, i16be as b16, i32be as b32
|
||||||
from PIL import _binary
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# declarations
|
# declarations
|
||||||
|
@ -42,12 +41,6 @@ BYTES_PER_ROW = [
|
||||||
lambda bits: ((bits+63) >> 3) & ~7,
|
lambda bits: ((bits+63) >> 3) & ~7,
|
||||||
]
|
]
|
||||||
|
|
||||||
i8 = _binary.i8
|
|
||||||
l16 = _binary.i16le
|
|
||||||
l32 = _binary.i32le
|
|
||||||
b16 = _binary.i16be
|
|
||||||
b32 = _binary.i32be
|
|
||||||
|
|
||||||
|
|
||||||
def sz(s, o):
|
def sz(s, o):
|
||||||
return s[o:s.index(b"\0", o)]
|
return s[o:s.index(b"\0", o)]
|
||||||
|
|
|
@ -28,14 +28,11 @@
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from PIL import Image, ImageFile, ImagePalette, _binary
|
from . import Image, ImageFile, ImagePalette
|
||||||
|
from ._binary import i8, i16le as i16, o8, o16le as o16
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
i8 = _binary.i8
|
|
||||||
i16 = _binary.i16le
|
|
||||||
o8 = _binary.o8
|
|
||||||
|
|
||||||
__version__ = "0.6"
|
__version__ = "0.6"
|
||||||
|
|
||||||
|
|
||||||
|
@ -123,8 +120,6 @@ SAVE = {
|
||||||
"RGB": (5, 8, 3, "RGB;L"),
|
"RGB": (5, 8, 3, "RGB;L"),
|
||||||
}
|
}
|
||||||
|
|
||||||
o16 = _binary.o16le
|
|
||||||
|
|
||||||
|
|
||||||
def _save(im, fp, filename, check=0):
|
def _save(im, fp, filename, check=0):
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,8 @@
|
||||||
# Image plugin for PDF images (output only).
|
# Image plugin for PDF images (output only).
|
||||||
##
|
##
|
||||||
|
|
||||||
from PIL import Image, ImageFile
|
from . import Image, ImageFile
|
||||||
from PIL._binary import i8
|
from ._binary import i8
|
||||||
import io
|
import io
|
||||||
|
|
||||||
__version__ = "0.4"
|
__version__ = "0.4"
|
||||||
|
|
|
@ -19,16 +19,14 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image, ImageFile, _binary
|
from . import Image, ImageFile
|
||||||
|
from ._binary import i16le as i16
|
||||||
|
|
||||||
__version__ = "0.1"
|
__version__ = "0.1"
|
||||||
|
|
||||||
#
|
#
|
||||||
# helpers
|
# helpers
|
||||||
|
|
||||||
i16 = _binary.i16le
|
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix):
|
def _accept(prefix):
|
||||||
return prefix[:4] == b"\200\350\000\000"
|
return prefix[:4] == b"\200\350\000\000"
|
||||||
|
|
||||||
|
|
|
@ -38,17 +38,14 @@ import re
|
||||||
import zlib
|
import zlib
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
from PIL import Image, ImageFile, ImagePalette, _binary
|
from . import Image, ImageFile, ImagePalette
|
||||||
|
from ._binary import i8, i16be as i16, i32be as i32, o8, o16be as o16, o32be as o32
|
||||||
|
|
||||||
__version__ = "0.9"
|
__version__ = "0.9"
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
i8 = _binary.i8
|
is_cid = re.compile(br"\w\w\w\w").match
|
||||||
i16 = _binary.i16be
|
|
||||||
i32 = _binary.i32be
|
|
||||||
|
|
||||||
is_cid = re.compile(b"\w\w\w\w").match
|
|
||||||
|
|
||||||
|
|
||||||
_MAGIC = b"\211PNG\r\n\032\n"
|
_MAGIC = b"\211PNG\r\n\032\n"
|
||||||
|
@ -132,7 +129,7 @@ class ChunkStream(object):
|
||||||
def call(self, cid, pos, length):
|
def call(self, cid, pos, length):
|
||||||
"Call the appropriate chunk handler"
|
"Call the appropriate chunk handler"
|
||||||
|
|
||||||
logger.debug("STREAM %s %s %s", cid, pos, length)
|
logger.debug("STREAM %r %s %s", cid, pos, length)
|
||||||
return getattr(self, "chunk_" + cid.decode('ascii'))(pos, length)
|
return getattr(self, "chunk_" + cid.decode('ascii'))(pos, length)
|
||||||
|
|
||||||
def crc(self, cid, data):
|
def crc(self, cid, data):
|
||||||
|
@ -148,10 +145,10 @@ class ChunkStream(object):
|
||||||
crc1 = Image.core.crc32(data, Image.core.crc32(cid))
|
crc1 = Image.core.crc32(data, Image.core.crc32(cid))
|
||||||
crc2 = i16(self.fp.read(2)), i16(self.fp.read(2))
|
crc2 = i16(self.fp.read(2)), i16(self.fp.read(2))
|
||||||
if crc1 != crc2:
|
if crc1 != crc2:
|
||||||
raise SyntaxError("broken PNG file (bad header checksum in %s)"
|
raise SyntaxError("broken PNG file (bad header checksum in %r)"
|
||||||
% cid)
|
% cid)
|
||||||
except struct.error:
|
except struct.error:
|
||||||
raise SyntaxError("broken PNG file (incomplete checksum in %s)"
|
raise SyntaxError("broken PNG file (incomplete checksum in %r)"
|
||||||
% cid)
|
% cid)
|
||||||
|
|
||||||
def crc_skip(self, cid, data):
|
def crc_skip(self, cid, data):
|
||||||
|
@ -309,7 +306,7 @@ class PngStream(ChunkStream):
|
||||||
# Compression method 1 byte (0)
|
# Compression method 1 byte (0)
|
||||||
# Compressed profile n bytes (zlib with deflate compression)
|
# Compressed profile n bytes (zlib with deflate compression)
|
||||||
i = s.find(b"\0")
|
i = s.find(b"\0")
|
||||||
logger.debug("iCCP profile name %s", s[:i])
|
logger.debug("iCCP profile name %r", s[:i])
|
||||||
logger.debug("Compression method %s", i8(s[i]))
|
logger.debug("Compression method %s", i8(s[i]))
|
||||||
comp_method = i8(s[i])
|
comp_method = i8(s[i])
|
||||||
if comp_method != 0:
|
if comp_method != 0:
|
||||||
|
@ -539,7 +536,7 @@ class PngImageFile(ImageFile.ImageFile):
|
||||||
except EOFError:
|
except EOFError:
|
||||||
break
|
break
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
logger.debug("%s %s %s (unknown)", cid, pos, length)
|
logger.debug("%r %s %s (unknown)", cid, pos, length)
|
||||||
s = ImageFile._safe_read(self.fp, length)
|
s = ImageFile._safe_read(self.fp, length)
|
||||||
|
|
||||||
self.png.crc(cid, s)
|
self.png.crc(cid, s)
|
||||||
|
@ -621,10 +618,6 @@ class PngImageFile(ImageFile.ImageFile):
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# PNG writer
|
# PNG writer
|
||||||
|
|
||||||
o8 = _binary.o8
|
|
||||||
o16 = _binary.o16be
|
|
||||||
o32 = _binary.o32be
|
|
||||||
|
|
||||||
_OUTMODES = {
|
_OUTMODES = {
|
||||||
# supported PIL modes, and corresponding rawmodes/bits/color combinations
|
# supported PIL modes, and corresponding rawmodes/bits/color combinations
|
||||||
"1": ("1", b'\x01\x00'),
|
"1": ("1", b'\x01\x00'),
|
||||||
|
@ -722,6 +715,32 @@ def _save(im, fp, filename, chunk=putchunk, check=0):
|
||||||
b'\0', # 11: filter category
|
b'\0', # 11: filter category
|
||||||
b'\0') # 12: interlace flag
|
b'\0') # 12: interlace flag
|
||||||
|
|
||||||
|
chunks = [b"cHRM", b"gAMA", b"sBIT", b"sRGB", b"tIME"]
|
||||||
|
|
||||||
|
icc = im.encoderinfo.get("icc_profile", im.info.get("icc_profile"))
|
||||||
|
if icc:
|
||||||
|
# ICC profile
|
||||||
|
# according to PNG spec, the iCCP chunk contains:
|
||||||
|
# Profile name 1-79 bytes (character string)
|
||||||
|
# Null separator 1 byte (null character)
|
||||||
|
# Compression method 1 byte (0)
|
||||||
|
# Compressed profile n bytes (zlib with deflate compression)
|
||||||
|
name = b"ICC Profile"
|
||||||
|
data = name + b"\0\0" + zlib.compress(icc)
|
||||||
|
chunk(fp, b"iCCP", data)
|
||||||
|
else:
|
||||||
|
chunks.remove(b"sRGB")
|
||||||
|
|
||||||
|
info = im.encoderinfo.get("pnginfo")
|
||||||
|
if info:
|
||||||
|
chunks_multiple_allowed = [b"sPLT", b"iTXt", b"tEXt", b"zTXt"]
|
||||||
|
for cid, data in info.chunks:
|
||||||
|
if cid in chunks:
|
||||||
|
chunks.remove(cid)
|
||||||
|
chunk(fp, cid, data)
|
||||||
|
elif cid in chunks_multiple_allowed:
|
||||||
|
chunk(fp, cid, data)
|
||||||
|
|
||||||
if im.mode == "P":
|
if im.mode == "P":
|
||||||
palette_byte_number = (2 ** bits) * 3
|
palette_byte_number = (2 ** bits) * 3
|
||||||
palette_bytes = im.im.getpalette("RGB")[:palette_byte_number]
|
palette_bytes = im.im.getpalette("RGB")[:palette_byte_number]
|
||||||
|
@ -768,20 +787,11 @@ def _save(im, fp, filename, chunk=putchunk, check=0):
|
||||||
|
|
||||||
info = im.encoderinfo.get("pnginfo")
|
info = im.encoderinfo.get("pnginfo")
|
||||||
if info:
|
if info:
|
||||||
|
chunks = [b"bKGD", b"hIST"]
|
||||||
for cid, data in info.chunks:
|
for cid, data in info.chunks:
|
||||||
chunk(fp, cid, data)
|
if cid in chunks:
|
||||||
|
chunks.remove(cid)
|
||||||
icc = im.encoderinfo.get("icc_profile", im.info.get("icc_profile"))
|
chunk(fp, cid, data)
|
||||||
if icc:
|
|
||||||
# ICC profile
|
|
||||||
# according to PNG spec, the iCCP chunk contains:
|
|
||||||
# Profile name 1-79 bytes (character string)
|
|
||||||
# Null separator 1 byte (null character)
|
|
||||||
# Compression method 1 byte (0)
|
|
||||||
# Compressed profile n bytes (zlib with deflate compression)
|
|
||||||
name = b"ICC Profile"
|
|
||||||
data = name + b"\0\0" + zlib.compress(icc)
|
|
||||||
chunk(fp, b"iCCP", data)
|
|
||||||
|
|
||||||
ImageFile._save(im, _idat(fp, chunk),
|
ImageFile._save(im, _idat(fp, chunk),
|
||||||
[("zip", (0, 0)+im.size, 0, rawmode)])
|
[("zip", (0, 0)+im.size, 0, rawmode)])
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
import string
|
import string
|
||||||
|
|
||||||
from PIL import Image, ImageFile
|
from . import Image, ImageFile
|
||||||
|
|
||||||
__version__ = "0.2"
|
__version__ = "0.2"
|
||||||
|
|
||||||
|
@ -123,11 +123,6 @@ class PpmImageFile(ImageFile.ImageFile):
|
||||||
self.fp.tell(),
|
self.fp.tell(),
|
||||||
(rawmode, 0, 1))]
|
(rawmode, 0, 1))]
|
||||||
|
|
||||||
# ALTERNATIVE: load via builtin debug function
|
|
||||||
# self.im = Image.core.open_ppm(self.filename)
|
|
||||||
# self.mode = self.im.mode
|
|
||||||
# self.size = self.im.size
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
|
|
|
@ -18,7 +18,8 @@
|
||||||
|
|
||||||
__version__ = "0.4"
|
__version__ = "0.4"
|
||||||
|
|
||||||
from PIL import Image, ImageFile, ImagePalette, _binary
|
from . import Image, ImageFile, ImagePalette
|
||||||
|
from ._binary import i8, i16be as i16, i32be as i32
|
||||||
|
|
||||||
MODES = {
|
MODES = {
|
||||||
# (photoshop mode, bits) -> (pil mode, required channels)
|
# (photoshop mode, bits) -> (pil mode, required channels)
|
||||||
|
@ -33,13 +34,6 @@ MODES = {
|
||||||
(9, 8): ("LAB", 3)
|
(9, 8): ("LAB", 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
#
|
|
||||||
# helpers
|
|
||||||
|
|
||||||
i8 = _binary.i8
|
|
||||||
i16 = _binary.i16be
|
|
||||||
i32 = _binary.i32be
|
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------.
|
# --------------------------------------------------------------------.
|
||||||
# read PSD images
|
# read PSD images
|
||||||
|
|
|
@ -7,9 +7,12 @@
|
||||||
# See "The SGI Image File Format (Draft version 0.97)", Paul Haeberli.
|
# See "The SGI Image File Format (Draft version 0.97)", Paul Haeberli.
|
||||||
# <ftp://ftp.sgi.com/graphics/SGIIMAGESPEC>
|
# <ftp://ftp.sgi.com/graphics/SGIIMAGESPEC>
|
||||||
#
|
#
|
||||||
|
#
|
||||||
# History:
|
# History:
|
||||||
|
# 2016-16-10 mb Add save method without compression
|
||||||
# 1995-09-10 fl Created
|
# 1995-09-10 fl Created
|
||||||
#
|
#
|
||||||
|
# Copyright (c) 2016 by Mickael Bonfill.
|
||||||
# Copyright (c) 2008 by Karsten Hiddemann.
|
# Copyright (c) 2008 by Karsten Hiddemann.
|
||||||
# Copyright (c) 1997 by Secret Labs AB.
|
# Copyright (c) 1997 by Secret Labs AB.
|
||||||
# Copyright (c) 1995 by Fredrik Lundh.
|
# Copyright (c) 1995 by Fredrik Lundh.
|
||||||
|
@ -18,12 +21,12 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
from PIL import Image, ImageFile, _binary
|
from . import Image, ImageFile
|
||||||
|
from ._binary import i8, o8, i16be as i16
|
||||||
|
import struct
|
||||||
|
import os
|
||||||
|
|
||||||
__version__ = "0.2"
|
__version__ = "0.3"
|
||||||
|
|
||||||
i8 = _binary.i8
|
|
||||||
i16 = _binary.i16be
|
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix):
|
def _accept(prefix):
|
||||||
|
@ -76,12 +79,79 @@ class SgiImageFile(ImageFile.ImageFile):
|
||||||
elif compression == 1:
|
elif compression == 1:
|
||||||
raise ValueError("SGI RLE encoding not supported")
|
raise ValueError("SGI RLE encoding not supported")
|
||||||
|
|
||||||
|
|
||||||
|
def _save(im, fp, filename):
|
||||||
|
if im.mode != "RGB" and im.mode != "RGBA" and im.mode != "L":
|
||||||
|
raise ValueError("Unsupported SGI image mode")
|
||||||
|
|
||||||
|
# Flip the image, since the origin of SGI file is the bottom-left corner
|
||||||
|
im = im.transpose(Image.FLIP_TOP_BOTTOM)
|
||||||
|
# Define the file as SGI File Format
|
||||||
|
magicNumber = 474
|
||||||
|
# Run-Length Encoding Compression - Unsupported at this time
|
||||||
|
rle = 0
|
||||||
|
# Byte-per-pixel precision, 1 = 8bits per pixel
|
||||||
|
bpc = 1
|
||||||
|
# Number of dimensions (x,y,z)
|
||||||
|
dim = 3
|
||||||
|
# X Dimension = width / Y Dimension = height
|
||||||
|
x, y = im.size
|
||||||
|
if im.mode == "L" and y == 1:
|
||||||
|
dim = 1
|
||||||
|
elif im.mode == "L":
|
||||||
|
dim = 2
|
||||||
|
# Z Dimension: Number of channels
|
||||||
|
z = len(im.mode)
|
||||||
|
if dim == 1 or dim == 2:
|
||||||
|
z = 1
|
||||||
|
# Minimum Byte value
|
||||||
|
pinmin = 0
|
||||||
|
# Maximum Byte value (255 = 8bits per pixel)
|
||||||
|
pinmax = 255
|
||||||
|
# Image name (79 characters max, truncated below in write)
|
||||||
|
imgName = os.path.splitext(os.path.basename(filename))[0]
|
||||||
|
if str is not bytes:
|
||||||
|
imgName = imgName.encode('ascii', 'ignore')
|
||||||
|
# Standard representation of pixel in the file
|
||||||
|
colormap = 0
|
||||||
|
fp.write(struct.pack('>h', magicNumber))
|
||||||
|
fp.write(o8(rle))
|
||||||
|
fp.write(o8(bpc))
|
||||||
|
fp.write(struct.pack('>H', dim))
|
||||||
|
fp.write(struct.pack('>H', x))
|
||||||
|
fp.write(struct.pack('>H', y))
|
||||||
|
fp.write(struct.pack('>H', z))
|
||||||
|
fp.write(struct.pack('>l', pinmin))
|
||||||
|
fp.write(struct.pack('>l', pinmax))
|
||||||
|
|
||||||
|
fp.write(struct.pack('4s', b'')) # dummy
|
||||||
|
fp.write(struct.pack('79s', imgName)) # truncates to 79 chars
|
||||||
|
fp.write(struct.pack('s', b'')) # force null byte after imgname
|
||||||
|
fp.write(struct.pack('>l', colormap))
|
||||||
|
|
||||||
|
fp.write(struct.pack('404s', b'')) # dummy
|
||||||
|
|
||||||
|
#assert we've got the right number of bands.
|
||||||
|
if len(im.getbands()) != z:
|
||||||
|
raise ValueError("incorrect number of bands in SGI write: %s vs %s" %
|
||||||
|
(z, len(im.getbands())))
|
||||||
|
|
||||||
|
for channel in im.split():
|
||||||
|
fp.write(channel.tobytes())
|
||||||
|
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# registry
|
# registry
|
||||||
|
|
||||||
Image.register_open(SgiImageFile.format, SgiImageFile, _accept)
|
Image.register_open(SgiImageFile.format, SgiImageFile, _accept)
|
||||||
|
Image.register_save(SgiImageFile.format, _save)
|
||||||
|
Image.register_mime(SgiImageFile.format, "image/sgi")
|
||||||
|
Image.register_mime(SgiImageFile.format, "image/rgb")
|
||||||
Image.register_extension(SgiImageFile.format, ".bw")
|
Image.register_extension(SgiImageFile.format, ".bw")
|
||||||
Image.register_extension(SgiImageFile.format, ".rgb")
|
Image.register_extension(SgiImageFile.format, ".rgb")
|
||||||
Image.register_extension(SgiImageFile.format, ".rgba")
|
Image.register_extension(SgiImageFile.format, ".rgba")
|
||||||
Image.register_extension(SgiImageFile.format, ".sgi")
|
Image.register_extension(SgiImageFile.format, ".sgi")
|
||||||
|
|
||||||
|
# End of file
|
||||||
|
|
|
@ -58,7 +58,7 @@ iforms = [1, 3, -11, -12, -21, -22]
|
||||||
|
|
||||||
# There is no magic number to identify Spider files, so just check a
|
# There is no magic number to identify Spider files, so just check a
|
||||||
# series of header locations to see if they have reasonable values.
|
# series of header locations to see if they have reasonable values.
|
||||||
# Returns no.of bytes in the header, if it is a valid Spider header,
|
# Returns no. of bytes in the header, if it is a valid Spider header,
|
||||||
# otherwise returns 0
|
# otherwise returns 0
|
||||||
|
|
||||||
def isSpiderHeader(t):
|
def isSpiderHeader(t):
|
||||||
|
@ -75,7 +75,7 @@ def isSpiderHeader(t):
|
||||||
labrec = int(h[13]) # no. records in file header
|
labrec = int(h[13]) # no. records in file header
|
||||||
labbyt = int(h[22]) # total no. of bytes in header
|
labbyt = int(h[22]) # total no. of bytes in header
|
||||||
lenbyt = int(h[23]) # record length in bytes
|
lenbyt = int(h[23]) # record length in bytes
|
||||||
# print "labrec = %d, labbyt = %d, lenbyt = %d" % (labrec,labbyt,lenbyt)
|
# print("labrec = %d, labbyt = %d, lenbyt = %d" % (labrec,labbyt,lenbyt))
|
||||||
if labbyt != (labrec * lenbyt):
|
if labbyt != (labrec * lenbyt):
|
||||||
return 0
|
return 0
|
||||||
# looks like a valid header
|
# looks like a valid header
|
||||||
|
@ -83,9 +83,8 @@ def isSpiderHeader(t):
|
||||||
|
|
||||||
|
|
||||||
def isSpiderImage(filename):
|
def isSpiderImage(filename):
|
||||||
fp = open(filename, 'rb')
|
with open(filename, 'rb') as fp:
|
||||||
f = fp.read(92) # read 23 * 4 bytes
|
f = fp.read(92) # read 23 * 4 bytes
|
||||||
fp.close()
|
|
||||||
t = struct.unpack('>23f', f) # try big-endian first
|
t = struct.unpack('>23f', f) # try big-endian first
|
||||||
hdrlen = isSpiderHeader(t)
|
hdrlen = isSpiderHeader(t)
|
||||||
if hdrlen == 0:
|
if hdrlen == 0:
|
||||||
|
|
|
@ -17,12 +17,11 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
from PIL import Image, ImageFile, ImagePalette, _binary
|
from . import Image, ImageFile, ImagePalette
|
||||||
|
from ._binary import i32be as i32
|
||||||
|
|
||||||
__version__ = "0.3"
|
__version__ = "0.3"
|
||||||
|
|
||||||
i32 = _binary.i32be
|
|
||||||
|
|
||||||
|
|
||||||
def _accept(prefix):
|
def _accept(prefix):
|
||||||
return len(prefix) >= 4 and i32(prefix) == 0x59a66a95
|
return len(prefix) >= 4 and i32(prefix) == 0x59a66a95
|
||||||
|
@ -38,6 +37,21 @@ class SunImageFile(ImageFile.ImageFile):
|
||||||
|
|
||||||
def _open(self):
|
def _open(self):
|
||||||
|
|
||||||
|
# The Sun Raster file header is 32 bytes in length and has the following format:
|
||||||
|
|
||||||
|
# typedef struct _SunRaster
|
||||||
|
# {
|
||||||
|
# DWORD MagicNumber; /* Magic (identification) number */
|
||||||
|
# DWORD Width; /* Width of image in pixels */
|
||||||
|
# DWORD Height; /* Height of image in pixels */
|
||||||
|
# DWORD Depth; /* Number of bits per pixel */
|
||||||
|
# DWORD Length; /* Size of image data in bytes */
|
||||||
|
# DWORD Type; /* Type of raster file */
|
||||||
|
# DWORD ColorMapType; /* Type of color map */
|
||||||
|
# DWORD ColorMapLength; /* Size of the color map in bytes */
|
||||||
|
# } SUNRASTER;
|
||||||
|
|
||||||
|
|
||||||
# HEAD
|
# HEAD
|
||||||
s = self.fp.read(32)
|
s = self.fp.read(32)
|
||||||
if i32(s) != 0x59a66a95:
|
if i32(s) != 0x59a66a95:
|
||||||
|
@ -48,30 +62,70 @@ class SunImageFile(ImageFile.ImageFile):
|
||||||
self.size = i32(s[4:8]), i32(s[8:12])
|
self.size = i32(s[4:8]), i32(s[8:12])
|
||||||
|
|
||||||
depth = i32(s[12:16])
|
depth = i32(s[12:16])
|
||||||
|
data_length = i32(s[16:20]) # unreliable, ignore.
|
||||||
|
file_type = i32(s[20:24])
|
||||||
|
palette_type = i32(s[24:28]) # 0: None, 1: RGB, 2: Raw/arbitrary
|
||||||
|
palette_length = i32(s[28:32])
|
||||||
|
|
||||||
if depth == 1:
|
if depth == 1:
|
||||||
self.mode, rawmode = "1", "1;I"
|
self.mode, rawmode = "1", "1;I"
|
||||||
|
elif depth == 4:
|
||||||
|
self.mode, rawmode = "L", "L;4"
|
||||||
elif depth == 8:
|
elif depth == 8:
|
||||||
self.mode = rawmode = "L"
|
self.mode = rawmode = "L"
|
||||||
elif depth == 24:
|
elif depth == 24:
|
||||||
self.mode, rawmode = "RGB", "BGR"
|
if file_type == 3:
|
||||||
|
self.mode, rawmode = "RGB", "RGB"
|
||||||
|
else:
|
||||||
|
self.mode, rawmode = "RGB", "BGR"
|
||||||
|
elif depth == 32:
|
||||||
|
if file_type == 3:
|
||||||
|
self.mode, rawmode = 'RGB', 'RGBX'
|
||||||
|
else:
|
||||||
|
self.mode, rawmode = 'RGB', 'BGRX'
|
||||||
else:
|
else:
|
||||||
raise SyntaxError("unsupported mode")
|
raise SyntaxError("Unsupported Mode/Bit Depth")
|
||||||
|
|
||||||
compression = i32(s[20:24])
|
if palette_length:
|
||||||
|
if palette_length > 1024:
|
||||||
|
raise SyntaxError("Unsupported Color Palette Length")
|
||||||
|
|
||||||
if i32(s[24:28]) != 0:
|
if palette_type != 1:
|
||||||
length = i32(s[28:32])
|
raise SyntaxError("Unsupported Palette Type")
|
||||||
offset = offset + length
|
|
||||||
self.palette = ImagePalette.raw("RGB;L", self.fp.read(length))
|
offset = offset + palette_length
|
||||||
|
self.palette = ImagePalette.raw("RGB;L", self.fp.read(palette_length))
|
||||||
if self.mode == "L":
|
if self.mode == "L":
|
||||||
self.mode = rawmode = "P"
|
self.mode = "P"
|
||||||
|
rawmode = rawmode.replace('L', 'P')
|
||||||
|
|
||||||
stride = (((self.size[0] * depth + 7) // 8) + 3) & (~3)
|
# 16 bit boundaries on stride
|
||||||
|
stride = ((self.size[0] * depth + 15) // 16) * 2
|
||||||
|
|
||||||
if compression == 1:
|
# file type: Type is the version (or flavor) of the bitmap
|
||||||
|
# file. The following values are typically found in the Type
|
||||||
|
# field:
|
||||||
|
# 0000h Old
|
||||||
|
# 0001h Standard
|
||||||
|
# 0002h Byte-encoded
|
||||||
|
# 0003h RGB format
|
||||||
|
# 0004h TIFF format
|
||||||
|
# 0005h IFF format
|
||||||
|
# FFFFh Experimental
|
||||||
|
|
||||||
|
# Old and standard are the same, except for the length tag.
|
||||||
|
# byte-encoded is run-length-encoded
|
||||||
|
# RGB looks similar to standard, but RGB byte order
|
||||||
|
# TIFF and IFF mean that they were converted from T/IFF
|
||||||
|
# Experimental means that it's something else.
|
||||||
|
# (http://www.fileformat.info/format/sunraster/egff.htm)
|
||||||
|
|
||||||
|
if file_type in (0, 1, 3, 4, 5):
|
||||||
self.tile = [("raw", (0, 0)+self.size, offset, (rawmode, stride))]
|
self.tile = [("raw", (0, 0)+self.size, offset, (rawmode, stride))]
|
||||||
elif compression == 2:
|
elif file_type == 2:
|
||||||
self.tile = [("sun_rle", (0, 0)+self.size, offset, rawmode)]
|
self.tile = [("sun_rle", (0, 0)+self.size, offset, rawmode)]
|
||||||
|
else:
|
||||||
|
raise SyntaxError('Unsupported Sun Raster file type')
|
||||||
|
|
||||||
#
|
#
|
||||||
# registry
|
# registry
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import ContainerIO
|
from . import ContainerIO
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
|
@ -17,7 +17,8 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
from PIL import Image, ImageFile, ImagePalette, _binary
|
from . import Image, ImageFile, ImagePalette
|
||||||
|
from ._binary import i8, i16le as i16, o8, o16le as o16, o32le as o32
|
||||||
|
|
||||||
__version__ = "0.3"
|
__version__ = "0.3"
|
||||||
|
|
||||||
|
@ -26,9 +27,6 @@ __version__ = "0.3"
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Read RGA file
|
# Read RGA file
|
||||||
|
|
||||||
i8 = _binary.i8
|
|
||||||
i16 = _binary.i16le
|
|
||||||
|
|
||||||
|
|
||||||
MODES = {
|
MODES = {
|
||||||
# map imagetype/depth to rawmode
|
# map imagetype/depth to rawmode
|
||||||
|
@ -132,10 +130,6 @@ class TgaImageFile(ImageFile.ImageFile):
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Write TGA file
|
# Write TGA file
|
||||||
|
|
||||||
o8 = _binary.o8
|
|
||||||
o16 = _binary.o16le
|
|
||||||
o32 = _binary.o32le
|
|
||||||
|
|
||||||
SAVE = {
|
SAVE = {
|
||||||
"1": ("1", 1, 0, 3),
|
"1": ("1", 1, 0, 3),
|
||||||
"L": ("L", 8, 0, 3),
|
"L": ("L", 8, 0, 3),
|
||||||
|
|
|
@ -41,10 +41,8 @@
|
||||||
|
|
||||||
from __future__ import division, print_function
|
from __future__ import division, print_function
|
||||||
|
|
||||||
from PIL import Image, ImageFile
|
from . import Image, ImageFile, ImagePalette, TiffTags
|
||||||
from PIL import ImagePalette
|
from ._binary import i8, o8
|
||||||
from PIL import _binary
|
|
||||||
from PIL import TiffTags
|
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
|
@ -71,9 +69,6 @@ IFD_LEGACY_API = True
|
||||||
II = b"II" # little-endian (Intel style)
|
II = b"II" # little-endian (Intel style)
|
||||||
MM = b"MM" # big-endian (Motorola style)
|
MM = b"MM" # big-endian (Motorola style)
|
||||||
|
|
||||||
i8 = _binary.i8
|
|
||||||
o8 = _binary.o8
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Read TIFF files
|
# Read TIFF files
|
||||||
|
@ -132,7 +127,7 @@ COMPRESSION_INFO = {
|
||||||
34677: "tiff_sgilog24",
|
34677: "tiff_sgilog24",
|
||||||
}
|
}
|
||||||
|
|
||||||
COMPRESSION_INFO_REV = dict([(v, k) for (k, v) in COMPRESSION_INFO.items()])
|
COMPRESSION_INFO_REV = {v: k for k, v in COMPRESSION_INFO.items()}
|
||||||
|
|
||||||
OPEN_INFO = {
|
OPEN_INFO = {
|
||||||
# (ByteOrder, PhotoInterpretation, SampleFormat, FillOrder, BitsPerSample,
|
# (ByteOrder, PhotoInterpretation, SampleFormat, FillOrder, BitsPerSample,
|
||||||
|
@ -278,12 +273,12 @@ class IFDRational(Rational):
|
||||||
self._numerator = value
|
self._numerator = value
|
||||||
self._val = float(1)
|
self._val = float(1)
|
||||||
|
|
||||||
if type(value) == Fraction:
|
if isinstance(value, Fraction):
|
||||||
self._numerator = value.numerator
|
self._numerator = value.numerator
|
||||||
self._denominator = value.denominator
|
self._denominator = value.denominator
|
||||||
self._val = value
|
self._val = value
|
||||||
|
|
||||||
if type(value) == IFDRational:
|
if isinstance(value, IFDRational):
|
||||||
self._denominator = value.denominator
|
self._denominator = value.denominator
|
||||||
self._numerator = value.numerator
|
self._numerator = value.numerator
|
||||||
self._val = value._val
|
self._val = value._val
|
||||||
|
@ -294,11 +289,7 @@ class IFDRational(Rational):
|
||||||
return
|
return
|
||||||
|
|
||||||
elif denominator == 1:
|
elif denominator == 1:
|
||||||
if sys.hexversion < 0x2070000 and type(value) == float:
|
self._val = Fraction(value)
|
||||||
# python 2.6 is different.
|
|
||||||
self._val = Fraction.from_float(value)
|
|
||||||
else:
|
|
||||||
self._val = Fraction(value)
|
|
||||||
else:
|
else:
|
||||||
self._val = Fraction(value, denominator)
|
self._val = Fraction(value, denominator)
|
||||||
|
|
||||||
|
@ -342,7 +333,7 @@ class IFDRational(Rational):
|
||||||
'rfloordiv','mod','rmod', 'pow','rpow', 'pos', 'neg',
|
'rfloordiv','mod','rmod', 'pow','rpow', 'pos', 'neg',
|
||||||
'abs', 'trunc', 'lt', 'gt', 'le', 'ge', 'nonzero',
|
'abs', 'trunc', 'lt', 'gt', 'le', 'ge', 'nonzero',
|
||||||
'ceil', 'floor', 'round']
|
'ceil', 'floor', 'round']
|
||||||
print "\n".join("__%s__ = _delegate('__%s__')" % (s,s) for s in a)
|
print("\n".join("__%s__ = _delegate('__%s__')" % (s,s) for s in a))
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__add__ = _delegate('__add__')
|
__add__ = _delegate('__add__')
|
||||||
|
@ -573,7 +564,7 @@ class ImageFileDirectory_v2(collections.MutableMapping):
|
||||||
|
|
||||||
def _register_loader(idx, size):
|
def _register_loader(idx, size):
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
from PIL.TiffTags import TYPES
|
from .TiffTags import TYPES
|
||||||
if func.__name__.startswith("load_"):
|
if func.__name__.startswith("load_"):
|
||||||
TYPES[idx] = func.__name__[5:].replace("_", " ")
|
TYPES[idx] = func.__name__[5:].replace("_", " ")
|
||||||
_load_dispatch[idx] = size, func
|
_load_dispatch[idx] = size, func
|
||||||
|
@ -587,12 +578,12 @@ class ImageFileDirectory_v2(collections.MutableMapping):
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
def _register_basic(idx_fmt_name):
|
def _register_basic(idx_fmt_name):
|
||||||
from PIL.TiffTags import TYPES
|
from .TiffTags import TYPES
|
||||||
idx, fmt, name = idx_fmt_name
|
idx, fmt, name = idx_fmt_name
|
||||||
TYPES[idx] = name
|
TYPES[idx] = name
|
||||||
size = struct.calcsize("=" + fmt)
|
size = struct.calcsize("=" + fmt)
|
||||||
_load_dispatch[idx] = size, lambda self, data, legacy_api=True: (
|
_load_dispatch[idx] = size, lambda self, data, legacy_api=True: (
|
||||||
self._unpack("{0}{1}".format(len(data) // size, fmt), data))
|
self._unpack("{}{}".format(len(data) // size, fmt), data))
|
||||||
_write_dispatch[idx] = lambda self, *values: (
|
_write_dispatch[idx] = lambda self, *values: (
|
||||||
b"".join(self._pack(fmt, value) for value in values))
|
b"".join(self._pack(fmt, value) for value in values))
|
||||||
|
|
||||||
|
@ -624,7 +615,7 @@ class ImageFileDirectory_v2(collections.MutableMapping):
|
||||||
|
|
||||||
@_register_loader(5, 8)
|
@_register_loader(5, 8)
|
||||||
def load_rational(self, data, legacy_api=True):
|
def load_rational(self, data, legacy_api=True):
|
||||||
vals = self._unpack("{0}L".format(len(data) // 4), data)
|
vals = self._unpack("{}L".format(len(data) // 4), data)
|
||||||
combine = lambda a, b: (a, b) if legacy_api else IFDRational(a, b)
|
combine = lambda a, b: (a, b) if legacy_api else IFDRational(a, b)
|
||||||
return tuple(combine(num, denom)
|
return tuple(combine(num, denom)
|
||||||
for num, denom in zip(vals[::2], vals[1::2]))
|
for num, denom in zip(vals[::2], vals[1::2]))
|
||||||
|
@ -644,7 +635,7 @@ class ImageFileDirectory_v2(collections.MutableMapping):
|
||||||
|
|
||||||
@_register_loader(10, 8)
|
@_register_loader(10, 8)
|
||||||
def load_signed_rational(self, data, legacy_api=True):
|
def load_signed_rational(self, data, legacy_api=True):
|
||||||
vals = self._unpack("{0}l".format(len(data) // 4), data)
|
vals = self._unpack("{}l".format(len(data) // 4), data)
|
||||||
combine = lambda a, b: (a, b) if legacy_api else IFDRational(a, b)
|
combine = lambda a, b: (a, b) if legacy_api else IFDRational(a, b)
|
||||||
return tuple(combine(num, denom)
|
return tuple(combine(num, denom)
|
||||||
for num, denom in zip(vals[::2], vals[1::2]))
|
for num, denom in zip(vals[::2], vals[1::2]))
|
||||||
|
@ -804,7 +795,7 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2):
|
||||||
ifd = ImageFileDirectory_v1()
|
ifd = ImageFileDirectory_v1()
|
||||||
ifd[key] = 'Some Data'
|
ifd[key] = 'Some Data'
|
||||||
ifd.tagtype[key] = 2
|
ifd.tagtype[key] = 2
|
||||||
print ifd[key]
|
print(ifd[key])
|
||||||
('Some Data',)
|
('Some Data',)
|
||||||
|
|
||||||
Also contains a dictionary of tag types as read from the tiff image file,
|
Also contains a dictionary of tag types as read from the tiff image file,
|
||||||
|
@ -1010,9 +1001,6 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||||
# Section 14: Differencing Predictor
|
# Section 14: Differencing Predictor
|
||||||
self.decoderconfig = (self.tag_v2[PREDICTOR],)
|
self.decoderconfig = (self.tag_v2[PREDICTOR],)
|
||||||
|
|
||||||
if ICCPROFILE in self.tag_v2:
|
|
||||||
self.info['icc_profile'] = self.tag_v2[ICCPROFILE]
|
|
||||||
|
|
||||||
return args
|
return args
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
|
@ -1056,7 +1044,7 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||||
# io.BytesIO have a fileno, but returns an IOError if
|
# io.BytesIO have a fileno, but returns an IOError if
|
||||||
# it doesn't use a file descriptor.
|
# it doesn't use a file descriptor.
|
||||||
fp = False
|
fp = False
|
||||||
|
|
||||||
if fp:
|
if fp:
|
||||||
args[2] = fp
|
args[2] = fp
|
||||||
|
|
||||||
|
@ -1175,11 +1163,15 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||||
yres = self.tag_v2.get(Y_RESOLUTION, 1)
|
yres = self.tag_v2.get(Y_RESOLUTION, 1)
|
||||||
|
|
||||||
if xres and yres:
|
if xres and yres:
|
||||||
resunit = self.tag_v2.get(RESOLUTION_UNIT, 1)
|
resunit = self.tag_v2.get(RESOLUTION_UNIT)
|
||||||
if resunit == 2: # dots per inch
|
if resunit == 2: # dots per inch
|
||||||
self.info["dpi"] = xres, yres
|
self.info["dpi"] = xres, yres
|
||||||
elif resunit == 3: # dots per centimeter. convert to dpi
|
elif resunit == 3: # dots per centimeter. convert to dpi
|
||||||
self.info["dpi"] = xres * 2.54, yres * 2.54
|
self.info["dpi"] = xres * 2.54, yres * 2.54
|
||||||
|
elif resunit == None: # used to default to 1, but now 2)
|
||||||
|
self.info["dpi"] = xres, yres
|
||||||
|
# For backward compatibility, we also preserve the old behavior.
|
||||||
|
self.info["resolution"] = xres, yres
|
||||||
else: # No absolute unit of measurement
|
else: # No absolute unit of measurement
|
||||||
self.info["resolution"] = xres, yres
|
self.info["resolution"] = xres, yres
|
||||||
|
|
||||||
|
@ -1201,7 +1193,7 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||||
"tiff_sgilog24",
|
"tiff_sgilog24",
|
||||||
"tiff_raw_16"]:
|
"tiff_raw_16"]:
|
||||||
# if DEBUG:
|
# if DEBUG:
|
||||||
# print "Activating g4 compression for whole file"
|
# print("Activating g4 compression for whole file")
|
||||||
|
|
||||||
# Decoder expects entire file as one tile.
|
# Decoder expects entire file as one tile.
|
||||||
# There's a buffer size limit in load (64k)
|
# There's a buffer size limit in load (64k)
|
||||||
|
@ -1246,12 +1238,12 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||||
a = None
|
a = None
|
||||||
|
|
||||||
else:
|
else:
|
||||||
for i in range(len(offsets)):
|
for i, offset in enumerate(offsets):
|
||||||
a = self._decoder(rawmode, l, i)
|
a = self._decoder(rawmode, l, i)
|
||||||
self.tile.append(
|
self.tile.append(
|
||||||
(self._compression,
|
(self._compression,
|
||||||
(0, min(y, ysize), w, min(y+h, ysize)),
|
(0, min(y, ysize), w, min(y+h, ysize)),
|
||||||
offsets[i], a))
|
offset, a))
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
print("tiles: ", self.tile)
|
print("tiles: ", self.tile)
|
||||||
y = y + h
|
y = y + h
|
||||||
|
@ -1285,6 +1277,10 @@ class TiffImageFile(ImageFile.ImageFile):
|
||||||
print("- unsupported data organization")
|
print("- unsupported data organization")
|
||||||
raise SyntaxError("unknown data organization")
|
raise SyntaxError("unknown data organization")
|
||||||
|
|
||||||
|
# Fix up info.
|
||||||
|
if ICCPROFILE in self.tag_v2:
|
||||||
|
self.info['icc_profile'] = self.tag_v2[ICCPROFILE]
|
||||||
|
|
||||||
# fixup palette descriptor
|
# fixup palette descriptor
|
||||||
|
|
||||||
if self.mode == "P":
|
if self.mode == "P":
|
||||||
|
@ -1366,10 +1362,10 @@ def _save(im, fp, filename):
|
||||||
ifd[key] = im.tag_v2[key]
|
ifd[key] = im.tag_v2[key]
|
||||||
ifd.tagtype[key] = im.tag_v2.tagtype[key]
|
ifd.tagtype[key] = im.tag_v2.tagtype[key]
|
||||||
|
|
||||||
# preserve ICC profile (should also work when saving other formats
|
# preserve ICC profile (should also work when saving other formats
|
||||||
# which support profiles as TIFF) -- 2008-06-06 Florian Hoech
|
# which support profiles as TIFF) -- 2008-06-06 Florian Hoech
|
||||||
if "icc_profile" in im.info:
|
if "icc_profile" in im.info:
|
||||||
ifd[ICCPROFILE] = im.info["icc_profile"]
|
ifd[ICCPROFILE] = im.info["icc_profile"]
|
||||||
|
|
||||||
for key, name in [(IMAGEDESCRIPTION, "description"),
|
for key, name in [(IMAGEDESCRIPTION, "description"),
|
||||||
(X_RESOLUTION, "resolution"),
|
(X_RESOLUTION, "resolution"),
|
||||||
|
@ -1518,7 +1514,7 @@ class AppendingTiffWriter:
|
||||||
# JPEGQTables = 519
|
# JPEGQTables = 519
|
||||||
# JPEGDCTables = 520
|
# JPEGDCTables = 520
|
||||||
# JPEGACTables = 521
|
# JPEGACTables = 521
|
||||||
Tags = set((273, 288, 324, 519, 520, 521))
|
Tags = {273, 288, 324, 519, 520, 521}
|
||||||
|
|
||||||
def __init__(self, fn, new=False):
|
def __init__(self, fn, new=False):
|
||||||
if hasattr(fn, 'read'):
|
if hasattr(fn, 'read'):
|
||||||
|
|
|
@ -418,13 +418,13 @@ TYPES = {}
|
||||||
|
|
||||||
# some of these are not in our TAGS_V2 dict and were included from tiff.h
|
# some of these are not in our TAGS_V2 dict and were included from tiff.h
|
||||||
|
|
||||||
LIBTIFF_CORE = set([255, 256, 257, 258, 259, 262, 263, 266, 274, 277,
|
LIBTIFF_CORE = {255, 256, 257, 258, 259, 262, 263, 266, 274, 277,
|
||||||
278, 280, 281, 340, 341, 282, 283, 284, 286, 287,
|
278, 280, 281, 340, 341, 282, 283, 284, 286, 287,
|
||||||
296, 297, 321, 320, 338, 32995, 322, 323, 32998,
|
296, 297, 321, 320, 338, 32995, 322, 323, 32998,
|
||||||
32996, 339, 32997, 330, 531, 530, 301, 532, 333,
|
32996, 339, 32997, 330, 531, 530, 301, 532, 333,
|
||||||
# as above
|
# as above
|
||||||
269 # this has been in our tests forever, and works
|
269 # this has been in our tests forever, and works
|
||||||
])
|
}
|
||||||
|
|
||||||
LIBTIFF_CORE.remove(320) # Array of short, crashes
|
LIBTIFF_CORE.remove(320) # Array of short, crashes
|
||||||
LIBTIFF_CORE.remove(301) # Array of short, crashes
|
LIBTIFF_CORE.remove(301) # Array of short, crashes
|
||||||
|
|
|
@ -23,7 +23,8 @@
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
from PIL import Image, _binary
|
from . import Image
|
||||||
|
from ._binary import i32le as i32
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import builtins
|
import builtins
|
||||||
|
@ -31,8 +32,6 @@ except ImportError:
|
||||||
import __builtin__
|
import __builtin__
|
||||||
builtins = __builtin__
|
builtins = __builtin__
|
||||||
|
|
||||||
i32 = _binary.i32le
|
|
||||||
|
|
||||||
|
|
||||||
def open(filename):
|
def open(filename):
|
||||||
"""
|
"""
|
||||||
|
@ -47,33 +46,35 @@ def open(filename):
|
||||||
# FIXME: modify to return a WalImageFile instance instead of
|
# FIXME: modify to return a WalImageFile instance instead of
|
||||||
# plain Image object ?
|
# plain Image object ?
|
||||||
|
|
||||||
|
def imopen(fp):
|
||||||
|
# read header fields
|
||||||
|
header = fp.read(32+24+32+12)
|
||||||
|
size = i32(header, 32), i32(header, 36)
|
||||||
|
offset = i32(header, 40)
|
||||||
|
|
||||||
|
# load pixel data
|
||||||
|
fp.seek(offset)
|
||||||
|
|
||||||
|
Image._decompression_bomb_check(size)
|
||||||
|
im = Image.frombytes("P", size, fp.read(size[0] * size[1]))
|
||||||
|
im.putpalette(quake2palette)
|
||||||
|
|
||||||
|
im.format = "WAL"
|
||||||
|
im.format_description = "Quake2 Texture"
|
||||||
|
|
||||||
|
# strings are null-terminated
|
||||||
|
im.info["name"] = header[:32].split(b"\0", 1)[0]
|
||||||
|
next_name = header[56:56+32].split(b"\0", 1)[0]
|
||||||
|
if next_name:
|
||||||
|
im.info["next_name"] = next_name
|
||||||
|
|
||||||
|
return im
|
||||||
|
|
||||||
if hasattr(filename, "read"):
|
if hasattr(filename, "read"):
|
||||||
fp = filename
|
return imopen(filename)
|
||||||
else:
|
else:
|
||||||
fp = builtins.open(filename, "rb")
|
with builtins.open(filename, "rb") as fp:
|
||||||
|
return imopen(fp)
|
||||||
# read header fields
|
|
||||||
header = fp.read(32+24+32+12)
|
|
||||||
size = i32(header, 32), i32(header, 36)
|
|
||||||
offset = i32(header, 40)
|
|
||||||
|
|
||||||
# load pixel data
|
|
||||||
fp.seek(offset)
|
|
||||||
|
|
||||||
im = Image.frombytes("P", size, fp.read(size[0] * size[1]))
|
|
||||||
im.putpalette(quake2palette)
|
|
||||||
|
|
||||||
im.format = "WAL"
|
|
||||||
im.format_description = "Quake2 Texture"
|
|
||||||
|
|
||||||
# strings are null-terminated
|
|
||||||
im.info["name"] = header[:32].split(b"\0", 1)[0]
|
|
||||||
next_name = header[56:56+32].split(b"\0", 1)[0]
|
|
||||||
if next_name:
|
|
||||||
im.info["next_name"] = next_name
|
|
||||||
|
|
||||||
return im
|
|
||||||
|
|
||||||
|
|
||||||
quake2palette = (
|
quake2palette = (
|
||||||
# default palette taken from piffo 0.93 by Hans Häggström
|
# default palette taken from piffo 0.93 by Hans Häggström
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
from PIL import Image
|
from . import Image, ImageFile, _webp
|
||||||
from PIL import ImageFile
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from PIL import _webp
|
|
||||||
|
|
||||||
|
|
||||||
_VALID_WEBP_MODES = {
|
_VALID_WEBP_MODES = {
|
||||||
|
@ -43,7 +41,7 @@ class WebPImageFile(ImageFile.ImageFile):
|
||||||
self.tile = [("raw", (0, 0) + self.size, 0, self.mode)]
|
self.tile = [("raw", (0, 0) + self.size, 0, self.mode)]
|
||||||
|
|
||||||
def _getexif(self):
|
def _getexif(self):
|
||||||
from PIL.JpegImagePlugin import _getexif
|
from .JpegImagePlugin import _getexif
|
||||||
return _getexif(self)
|
return _getexif(self)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,17 @@
|
||||||
#
|
#
|
||||||
# See the README file for information on usage and redistribution.
|
# See the README file for information on usage and redistribution.
|
||||||
#
|
#
|
||||||
|
# WMF/EMF reference documentation:
|
||||||
|
# https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-WMF/[MS-WMF].pdf
|
||||||
|
# http://wvware.sourceforge.net/caolan/index.html
|
||||||
|
# http://wvware.sourceforge.net/caolan/ora-wmf.html
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
from . import Image, ImageFile
|
||||||
|
from ._binary import i16le as word, si16le as short, i32le as dword, si32le as _long
|
||||||
|
|
||||||
|
|
||||||
from PIL import Image, ImageFile, _binary
|
|
||||||
|
|
||||||
__version__ = "0.2"
|
__version__ = "0.2"
|
||||||
|
|
||||||
|
@ -53,20 +62,6 @@ if hasattr(Image.core, "drawwmf"):
|
||||||
|
|
||||||
register_handler(WmfHandler())
|
register_handler(WmfHandler())
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
|
||||||
|
|
||||||
word = _binary.i16le
|
|
||||||
|
|
||||||
|
|
||||||
def short(c, o=0):
|
|
||||||
v = word(c, o)
|
|
||||||
if v >= 32768:
|
|
||||||
v -= 65536
|
|
||||||
return v
|
|
||||||
|
|
||||||
dword = _binary.i32le
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Read WMF file
|
# Read WMF file
|
||||||
|
@ -111,7 +106,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
|
||||||
|
|
||||||
self.info["dpi"] = 72
|
self.info["dpi"] = 72
|
||||||
|
|
||||||
# print self.mode, self.size, self.info
|
# print(self.mode, self.size, self.info)
|
||||||
|
|
||||||
# sanity check (standard metafile header)
|
# sanity check (standard metafile header)
|
||||||
if s[22:26] != b"\x01\x00\t\x00":
|
if s[22:26] != b"\x01\x00\t\x00":
|
||||||
|
@ -121,13 +116,13 @@ class WmfStubImageFile(ImageFile.StubImageFile):
|
||||||
# enhanced metafile
|
# enhanced metafile
|
||||||
|
|
||||||
# get bounding box
|
# get bounding box
|
||||||
x0 = dword(s, 8)
|
x0 = _long(s, 8)
|
||||||
y0 = dword(s, 12)
|
y0 = _long(s, 12)
|
||||||
x1 = dword(s, 16)
|
x1 = _long(s, 16)
|
||||||
y1 = dword(s, 20)
|
y1 = _long(s, 20)
|
||||||
|
|
||||||
# get frame (in 0.01 millimeter units)
|
# get frame (in 0.01 millimeter units)
|
||||||
frame = dword(s, 24), dword(s, 28), dword(s, 32), dword(s, 36)
|
frame = _long(s, 24), _long(s, 28), _long(s, 32), _long(s, 36)
|
||||||
|
|
||||||
# normalize size to 72 dots per inch
|
# normalize size to 72 dots per inch
|
||||||
size = x1 - x0, y1 - y0
|
size = x1 - x0, y1 - y0
|
||||||
|
|
|
@ -17,12 +17,11 @@
|
||||||
# FIXME: make save work (this requires quantization support)
|
# FIXME: make save work (this requires quantization support)
|
||||||
#
|
#
|
||||||
|
|
||||||
from PIL import Image, ImageFile, ImagePalette, _binary
|
from . import Image, ImageFile, ImagePalette
|
||||||
|
from ._binary import o8
|
||||||
|
|
||||||
__version__ = "0.1"
|
__version__ = "0.1"
|
||||||
|
|
||||||
o8 = _binary.o8
|
|
||||||
|
|
||||||
_MAGIC = b"P7 332"
|
_MAGIC = b"P7 332"
|
||||||
|
|
||||||
# standard color palette for thumbnails (RGB332)
|
# standard color palette for thumbnails (RGB332)
|
||||||
|
|
|
@ -20,13 +20,13 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from PIL import Image, ImageFile
|
from . import Image, ImageFile
|
||||||
|
|
||||||
__version__ = "0.6"
|
__version__ = "0.6"
|
||||||
|
|
||||||
# XBM header
|
# XBM header
|
||||||
xbm_head = re.compile(
|
xbm_head = re.compile(
|
||||||
b"\s*#define[ \t]+.*_width[ \t]+(?P<width>[0-9]+)[\r\n]+"
|
br"\s*#define[ \t]+.*_width[ \t]+(?P<width>[0-9]+)[\r\n]+"
|
||||||
b"#define[ \t]+.*_height[ \t]+(?P<height>[0-9]+)[\r\n]+"
|
b"#define[ \t]+.*_height[ \t]+(?P<height>[0-9]+)[\r\n]+"
|
||||||
b"(?P<hotspot>"
|
b"(?P<hotspot>"
|
||||||
b"#define[ \t]+[^_]*_x_hot[ \t]+(?P<xhot>[0-9]+)[\r\n]+"
|
b"#define[ \t]+[^_]*_x_hot[ \t]+(?P<xhot>[0-9]+)[\r\n]+"
|
||||||
|
|
|
@ -16,8 +16,8 @@
|
||||||
|
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from PIL import Image, ImageFile, ImagePalette
|
from . import Image, ImageFile, ImagePalette
|
||||||
from PIL._binary import i8, o8
|
from ._binary import i8, o8
|
||||||
|
|
||||||
__version__ = "0.2"
|
__version__ = "0.2"
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
# ;-)
|
# ;-)
|
||||||
|
|
||||||
VERSION = '1.1.7' # PIL version
|
VERSION = '1.1.7' # PIL version
|
||||||
PILLOW_VERSION = '3.4.0.dev0' # Pillow
|
PILLOW_VERSION = '4.1.0.dev0' # Pillow
|
||||||
|
|
||||||
__version__ = PILLOW_VERSION
|
__version__ = PILLOW_VERSION
|
||||||
|
|
||||||
|
|
|
@ -28,26 +28,43 @@ else:
|
||||||
|
|
||||||
|
|
||||||
# Input, le = little endian, be = big endian
|
# Input, le = little endian, be = big endian
|
||||||
# TODO: replace with more readable struct.unpack equivalent
|
|
||||||
def i16le(c, o=0):
|
def i16le(c, o=0):
|
||||||
"""
|
"""
|
||||||
Converts a 2-bytes (16 bits) string to an integer.
|
Converts a 2-bytes (16 bits) string to an unsigned integer.
|
||||||
|
|
||||||
c: string containing bytes to convert
|
c: string containing bytes to convert
|
||||||
o: offset of bytes to convert in string
|
o: offset of bytes to convert in string
|
||||||
"""
|
"""
|
||||||
return unpack("<H", c[o:o+2])[0]
|
return unpack("<H", c[o:o+2])[0]
|
||||||
|
|
||||||
|
def si16le(c, o=0):
|
||||||
|
"""
|
||||||
|
Converts a 2-bytes (16 bits) string to a signed integer.
|
||||||
|
|
||||||
|
c: string containing bytes to convert
|
||||||
|
o: offset of bytes to convert in string
|
||||||
|
"""
|
||||||
|
return unpack("<h", c[o:o+2])[0]
|
||||||
|
|
||||||
|
|
||||||
def i32le(c, o=0):
|
def i32le(c, o=0):
|
||||||
"""
|
"""
|
||||||
Converts a 4-bytes (32 bits) string to an integer.
|
Converts a 4-bytes (32 bits) string to an unsigned integer.
|
||||||
|
|
||||||
c: string containing bytes to convert
|
c: string containing bytes to convert
|
||||||
o: offset of bytes to convert in string
|
o: offset of bytes to convert in string
|
||||||
"""
|
"""
|
||||||
return unpack("<I", c[o:o+4])[0]
|
return unpack("<I", c[o:o+4])[0]
|
||||||
|
|
||||||
|
def si32le(c, o=0):
|
||||||
|
"""
|
||||||
|
Converts a 4-bytes (32 bits) string to a signed integer.
|
||||||
|
|
||||||
|
c: string containing bytes to convert
|
||||||
|
o: offset of bytes to convert in string
|
||||||
|
"""
|
||||||
|
return unpack("<i", c[o:o+4])[0]
|
||||||
|
|
||||||
|
|
||||||
def i16be(c, o=0):
|
def i16be(c, o=0):
|
||||||
return unpack(">H", c[o:o+2])[0]
|
return unpack(">H", c[o:o+2])[0]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from PIL import Image
|
from . import Image
|
||||||
|
|
||||||
modules = {
|
modules = {
|
||||||
"pil": "PIL._imaging",
|
"pil": "PIL._imaging",
|
||||||
|
@ -17,7 +17,7 @@ def check_module(feature):
|
||||||
module = modules[feature]
|
module = modules[feature]
|
||||||
|
|
||||||
method_to_call = None
|
method_to_call = None
|
||||||
if type(module) is tuple:
|
if isinstance(module, tuple):
|
||||||
module, method_to_call = module
|
module, method_to_call = module
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -10,10 +10,6 @@ Install::
|
||||||
|
|
||||||
pip install coverage nose
|
pip install coverage nose
|
||||||
|
|
||||||
If you're using Python 2.6, there's one additional dependency::
|
|
||||||
|
|
||||||
pip install unittest2
|
|
||||||
|
|
||||||
Execution
|
Execution
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
|
|
@ -7,13 +7,8 @@ class TestJ2kEncodeOverflow(PillowTestCase):
|
||||||
|
|
||||||
im = Image.new('RGBA', (1024, 131584))
|
im = Image.new('RGBA', (1024, 131584))
|
||||||
target = self.tempfile('temp.jpc')
|
target = self.tempfile('temp.jpc')
|
||||||
try:
|
with self.assertRaises(IOError):
|
||||||
im.save(target)
|
im.save(target)
|
||||||
self.assertTrue(False, "Expected IOError, save succeeded?")
|
|
||||||
except IOError as err:
|
|
||||||
self.assertTrue(True, "IOError is expected")
|
|
||||||
except Exception as err:
|
|
||||||
self.assertTrue(False, "Expected IOError, got %s" % type(err))
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -10,13 +10,9 @@ class TestLibtiffSegfault(PillowTestCase):
|
||||||
libtiff >= 4.0.0
|
libtiff >= 4.0.0
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
with self.assertRaises(IOError):
|
||||||
im = Image.open(TEST_FILE)
|
im = Image.open(TEST_FILE)
|
||||||
im.load()
|
im.load()
|
||||||
except IOError:
|
|
||||||
self.assertTrue(True, "Got expected IOError")
|
|
||||||
except Exception:
|
|
||||||
self.fail("Should have returned IOError")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -5,11 +5,22 @@ from __future__ import print_function
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import os
|
import os
|
||||||
|
import unittest
|
||||||
|
|
||||||
if sys.version_info[:2] <= (2, 6):
|
from PIL import Image, ImageMath
|
||||||
import unittest2 as unittest
|
|
||||||
else:
|
|
||||||
import unittest
|
def convert_to_comparable(a, b):
|
||||||
|
new_a, new_b = a, b
|
||||||
|
if a.mode == 'P':
|
||||||
|
new_a = Image.new('L', a.size)
|
||||||
|
new_b = Image.new('L', b.size)
|
||||||
|
new_a.putdata(a.getdata())
|
||||||
|
new_b.putdata(b.getdata())
|
||||||
|
elif a.mode == 'I;16':
|
||||||
|
new_a = a.convert('I')
|
||||||
|
new_b = b.convert('I')
|
||||||
|
return new_a, new_b
|
||||||
|
|
||||||
|
|
||||||
class PillowTestCase(unittest.TestCase):
|
class PillowTestCase(unittest.TestCase):
|
||||||
|
@ -49,7 +60,7 @@ class PillowTestCase(unittest.TestCase):
|
||||||
len(a), len(b),
|
len(a), len(b),
|
||||||
msg or "got length %s, expected %s" % (len(a), len(b)))
|
msg or "got length %s, expected %s" % (len(a), len(b)))
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
all([x == y for x, y in zip(a, b)]),
|
all(x == y for x, y in zip(a, b)),
|
||||||
msg or "got %s, expected %s" % (a, b))
|
msg or "got %s, expected %s" % (a, b))
|
||||||
except:
|
except:
|
||||||
self.assertEqual(a, b, msg)
|
self.assertEqual(a, b, msg)
|
||||||
|
@ -84,14 +95,13 @@ class PillowTestCase(unittest.TestCase):
|
||||||
a.size, b.size,
|
a.size, b.size,
|
||||||
msg or "got size %r, expected %r" % (a.size, b.size))
|
msg or "got size %r, expected %r" % (a.size, b.size))
|
||||||
|
|
||||||
|
a, b = convert_to_comparable(a, b)
|
||||||
|
|
||||||
diff = 0
|
diff = 0
|
||||||
try:
|
for ach, bch in zip(a.split(), b.split()):
|
||||||
ord(b'0')
|
chdiff = ImageMath.eval("abs(a - b)", a=ach, b=bch).convert('L')
|
||||||
for abyte, bbyte in zip(a.tobytes(), b.tobytes()):
|
diff += sum(i * num for i, num in enumerate(chdiff.histogram()))
|
||||||
diff += abs(ord(abyte)-ord(bbyte))
|
|
||||||
except:
|
|
||||||
for abyte, bbyte in zip(a.tobytes(), b.tobytes()):
|
|
||||||
diff += abs(abyte-bbyte)
|
|
||||||
ave_diff = float(diff)/(a.size[0]*a.size[1])
|
ave_diff = float(diff)/(a.size[0]*a.size[1])
|
||||||
self.assertGreaterEqual(
|
self.assertGreaterEqual(
|
||||||
epsilon, ave_diff,
|
epsilon, ave_diff,
|
||||||
|
@ -139,9 +149,13 @@ class PillowTestCase(unittest.TestCase):
|
||||||
if skip:
|
if skip:
|
||||||
self.skipTest(msg or "Known Bad Test")
|
self.skipTest(msg or "Known Bad Test")
|
||||||
|
|
||||||
|
def shortDescription(self):
|
||||||
|
# Prevents `nose -v` printing docstrings
|
||||||
|
return None
|
||||||
|
|
||||||
def tempfile(self, template):
|
def tempfile(self, template):
|
||||||
assert template[:5] in ("temp.", "temp_")
|
assert template[:5] in ("temp.", "temp_")
|
||||||
(fd, path) = tempfile.mkstemp(template[4:], template[:4])
|
fd, path = tempfile.mkstemp(template[4:], template[:4])
|
||||||
os.close(fd)
|
os.close(fd)
|
||||||
|
|
||||||
self.addCleanup(self.delete_tempfile, path)
|
self.addCleanup(self.delete_tempfile, path)
|
||||||
|
|
BIN
Tests/images/bmp/q/rgb32bf-xbgr.bmp
Normal file
BIN
Tests/images/bmp/q/rgb32bf-xbgr.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
BIN
Tests/images/drawing.emf
Normal file
BIN
Tests/images/drawing.emf
Normal file
Binary file not shown.
BIN
Tests/images/drawing.wmf
Normal file
BIN
Tests/images/drawing.wmf
Normal file
Binary file not shown.
BIN
Tests/images/drawing_emf_ref.png
Normal file
BIN
Tests/images/drawing_emf_ref.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
BIN
Tests/images/drawing_wmf_ref.png
Normal file
BIN
Tests/images/drawing_wmf_ref.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 531 B |
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Tests/images/hopper.sgi
Normal file
BIN
Tests/images/hopper.sgi
Normal file
Binary file not shown.
BIN
Tests/images/hopper_256x256.ico
Normal file
BIN
Tests/images/hopper_256x256.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 200 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user