removed trashes added movement added requirements

This commit is contained in:
Michal Starski 2019-04-01 14:10:14 +02:00
parent 86fdd1543f
commit 17847fc58a
1498 changed files with 209724 additions and 33 deletions

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"python.formatting.provider": "autopep8"
}

View File

@ -1,6 +1,18 @@
from DataModels.Cell import Cell from DataModels.Cell import Cell
from DataModels.Road import Road
class GC( Cell ):
def __init__( self, x, y, max_rubbish, yellow = 0, green = 0, blue = 0):
Cell.__init__(self, x, y, max_rubbish, yellow, green, blue )
class GC(Cell):
def __init__(self, x, y, max_rubbish, yellow=0, green=0, blue=0):
Cell.__init__(self, x, y, max_rubbish, yellow, green, blue)
def move(self, direction, environment):
x, y = [self.x, self.y]
movement = {
"right": (x + 1, y) if environment[x + 1][y] == Road else (x, y),
"left": (x - 1, y) if environment[x - 1][y] == Road else (x, y),
"up": (x, y + 1) if environment[x][y + 1] == Road else (x, y),
"down": (x, y - 1) if environment[x][y - 1] == Road else (x, y)
}
self.x, self.y = movement[direction]
print(self.x, self.y)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
c

Binary file not shown.

11
c.c
View File

@ -1,11 +0,0 @@
#include <stdio.h>
int main( void ) {
char c = NULL;
int b = c || 5;
printf("%d\r\n", b);
return 0;
}

76
env/bin/activate vendored Normal file
View File

@ -0,0 +1,76 @@
# This file must be used with "source bin/activate" *from bash*
# you cannot run it directly
deactivate () {
# reset old environment variables
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
PATH="${_OLD_VIRTUAL_PATH:-}"
export PATH
unset _OLD_VIRTUAL_PATH
fi
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
export PYTHONHOME
unset _OLD_VIRTUAL_PYTHONHOME
fi
# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
hash -r
fi
if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
PS1="${_OLD_VIRTUAL_PS1:-}"
export PS1
unset _OLD_VIRTUAL_PS1
fi
unset VIRTUAL_ENV
if [ ! "$1" = "nondestructive" ] ; then
# Self destruct!
unset -f deactivate
fi
}
# unset irrelevant variables
deactivate nondestructive
VIRTUAL_ENV="/Users/michals/Desktop/development/SZI2019SmieciarzWmi/env"
export VIRTUAL_ENV
_OLD_VIRTUAL_PATH="$PATH"
PATH="$VIRTUAL_ENV/bin:$PATH"
export PATH
# unset PYTHONHOME if set
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
# could use `if (set -u; : $PYTHONHOME) ;` in bash
if [ -n "${PYTHONHOME:-}" ] ; then
_OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
unset PYTHONHOME
fi
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
_OLD_VIRTUAL_PS1="${PS1:-}"
if [ "x(env) " != x ] ; then
PS1="(env) ${PS1:-}"
else
if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then
# special case for Aspen magic directories
# see http://www.zetadev.com/software/aspen/
PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1"
else
PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1"
fi
fi
export PS1
fi
# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
hash -r
fi

37
env/bin/activate.csh vendored Normal file
View File

@ -0,0 +1,37 @@
# This file must be used with "source bin/activate.csh" *from csh*.
# You cannot run it directly.
# Created by Davide Di Blasi <davidedb@gmail.com>.
# Ported to Python 3.3 venv by Andrew Svetlov <andrew.svetlov@gmail.com>
alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate'
# Unset irrelevant variables.
deactivate nondestructive
setenv VIRTUAL_ENV "/Users/michals/Desktop/development/SZI2019SmieciarzWmi/env"
set _OLD_VIRTUAL_PATH="$PATH"
setenv PATH "$VIRTUAL_ENV/bin:$PATH"
set _OLD_VIRTUAL_PROMPT="$prompt"
if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
if ("env" != "") then
set env_name = "env"
else
if (`basename "VIRTUAL_ENV"` == "__") then
# special case for Aspen magic directories
# see http://www.zetadev.com/software/aspen/
set env_name = `basename \`dirname "$VIRTUAL_ENV"\``
else
set env_name = `basename "$VIRTUAL_ENV"`
endif
endif
set prompt = "[$env_name] $prompt"
unset env_name
endif
alias pydoc python -m pydoc
rehash

75
env/bin/activate.fish vendored Normal file
View File

@ -0,0 +1,75 @@
# This file must be used with ". bin/activate.fish" *from fish* (http://fishshell.org)
# you cannot run it directly
function deactivate -d "Exit virtualenv and return to normal shell environment"
# reset old environment variables
if test -n "$_OLD_VIRTUAL_PATH"
set -gx PATH $_OLD_VIRTUAL_PATH
set -e _OLD_VIRTUAL_PATH
end
if test -n "$_OLD_VIRTUAL_PYTHONHOME"
set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
set -e _OLD_VIRTUAL_PYTHONHOME
end
if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
functions -e fish_prompt
set -e _OLD_FISH_PROMPT_OVERRIDE
functions -c _old_fish_prompt fish_prompt
functions -e _old_fish_prompt
end
set -e VIRTUAL_ENV
if test "$argv[1]" != "nondestructive"
# Self destruct!
functions -e deactivate
end
end
# unset irrelevant variables
deactivate nondestructive
set -gx VIRTUAL_ENV "/Users/michals/Desktop/development/SZI2019SmieciarzWmi/env"
set -gx _OLD_VIRTUAL_PATH $PATH
set -gx PATH "$VIRTUAL_ENV/bin" $PATH
# unset PYTHONHOME if set
if set -q PYTHONHOME
set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
set -e PYTHONHOME
end
if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
# fish uses a function instead of an env var to generate the prompt.
# save the current fish_prompt function as the function _old_fish_prompt
functions -c fish_prompt _old_fish_prompt
# with the original prompt function renamed, we can override with our own.
function fish_prompt
# Save the return status of the last command
set -l old_status $status
# Prompt override?
if test -n "(env) "
printf "%s%s" "(env) " (set_color normal)
else
# ...Otherwise, prepend env
set -l _checkbase (basename "$VIRTUAL_ENV")
if test $_checkbase = "__"
# special case for Aspen magic directories
# see http://www.zetadev.com/software/aspen/
printf "%s[%s]%s " (set_color -b blue white) (basename (dirname "$VIRTUAL_ENV")) (set_color normal)
else
printf "%s(%s)%s" (set_color -b blue white) (basename "$VIRTUAL_ENV") (set_color normal)
end
end
# Restore the return status of the previous command.
echo "exit $old_status" | .
_old_fish_prompt
end
set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
end

11
env/bin/easy_install vendored Executable file
View File

@ -0,0 +1,11 @@
#!/Users/michals/Desktop/development/SZI2019SmieciarzWmi/env/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from setuptools.command.easy_install import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
sys.exit(main())

11
env/bin/easy_install-3.7 vendored Executable file
View File

@ -0,0 +1,11 @@
#!/Users/michals/Desktop/development/SZI2019SmieciarzWmi/env/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from setuptools.command.easy_install import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
sys.exit(main())

11
env/bin/pip vendored Executable file
View File

@ -0,0 +1,11 @@
#!/Users/michals/Desktop/development/SZI2019SmieciarzWmi/env/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from pip._internal import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
sys.exit(main())

11
env/bin/pip3 vendored Executable file
View File

@ -0,0 +1,11 @@
#!/Users/michals/Desktop/development/SZI2019SmieciarzWmi/env/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from pip._internal import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
sys.exit(main())

11
env/bin/pip3.7 vendored Executable file
View File

@ -0,0 +1,11 @@
#!/Users/michals/Desktop/development/SZI2019SmieciarzWmi/env/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from pip._internal import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
sys.exit(main())

1
env/bin/python vendored Symbolic link
View File

@ -0,0 +1 @@
python3

1
env/bin/python3 vendored Symbolic link
View File

@ -0,0 +1 @@
/Library/Frameworks/Python.framework/Versions/3.7/bin/python3

View File

@ -0,0 +1,27 @@
/*
pygame - Python Game Library
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _CAMERA_H
#define _CAMERA_H
#include "_pygame.h"
#include "camera.h"
#endif

View File

@ -0,0 +1,864 @@
/*
pygame - Python Game Library
Copyright (C) 2000-2001 Pete Shinners
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Pete Shinners
pete@shinners.org
*/
#ifndef _PYGAME_H
#define _PYGAME_H
/** This header file includes all the definitions for the
** base pygame extensions. This header only requires
** SDL and Python includes. The reason for functions
** prototyped with #define's is to allow for maximum
** python portability. It also uses python as the
** runtime linker, which allows for late binding. For more
** information on this style of development, read the Python
** docs on this subject.
** http://www.python.org/doc/current/ext/using-cobjects.html
**
** If using this to build your own derived extensions,
** you'll see that the functions available here are mainly
** used to help convert between python objects and SDL objects.
** Since this library doesn't add a lot of functionality to
** the SDL libarary, it doesn't need to offer a lot either.
**
** When initializing your extension module, you must manually
** import the modules you want to use. (this is the part about
** using python as the runtime linker). Each module has its
** own import_xxx() routine. You need to perform this import
** after you have initialized your own module, and before
** you call any routines from that module. Since every module
** in pygame does this, there are plenty of examples.
**
** The base module does include some useful conversion routines
** that you are free to use in your own extension.
**
** When making changes, it is very important to keep the
** FIRSTSLOT and NUMSLOT constants up to date for each
** section. Also be sure not to overlap any of the slots.
** When you do make a mistake with this, it will result
** is a dereferenced NULL pointer that is easier to diagnose
** than it could be :]
**/
#if defined(HAVE_SNPRINTF) /* defined in python.h (pyerrors.h) and SDL.h \
(SDL_config.h) */
#undef HAVE_SNPRINTF /* remove GCC redefine warning */
#endif
// This must be before all else
#if defined(__SYMBIAN32__) && defined(OPENC)
#include <sys/types.h>
#if defined(__WINS__)
void *
_alloca(size_t size);
#define alloca _alloca
#endif
#endif
#define PG_STRINGIZE_HELPER(x) #x
#define PG_STRINGIZE(x) PG_STRINGIZE_HELPER(x)
#define PG_WARN(desc) message(__FILE__ "(" PG_STRINGIZE(__LINE__) "): WARNING: " #desc)
/* This is unconditionally defined in Python.h */
#if defined(_POSIX_C_SOURCE)
#undef _POSIX_C_SOURCE
#endif
#include <Python.h>
/* the version macros are defined since version 1.9.5 */
#define PG_MAJOR_VERSION 1
#define PG_MINOR_VERSION 9
#define PG_PATCH_VERSION 5
#define PG_VERSIONNUM(MAJOR, MINOR, PATCH) (1000*(MAJOR) + 100*(MINOR) + (PATCH))
#define PG_VERSION_ATLEAST(MAJOR, MINOR, PATCH) \
(PG_VERSIONNUM(PG_MAJOR_VERSION, PG_MINOR_VERSION, PG_PATCH_VERSION) >= \
PG_VERSIONNUM(MAJOR, MINOR, PATCH))
/* Cobjects vanish in Python 3.2; so we will code as though we use capsules */
#if defined(Py_CAPSULE_H)
#define PG_HAVE_CAPSULE 1
#else
#define PG_HAVE_CAPSULE 0
#endif
#if defined(Py_COBJECT_H)
#define PG_HAVE_COBJECT 1
#else
#define PG_HAVE_COBJECT 0
#endif
#if !PG_HAVE_CAPSULE
#define PyCapsule_New(ptr, n, dfn) PyCObject_FromVoidPtr(ptr, dfn)
#define PyCapsule_GetPointer(obj, n) PyCObject_AsVoidPtr(obj)
#define PyCapsule_CheckExact(obj) PyCObject_Check(obj)
#endif
/* Pygame uses Py_buffer (PEP 3118) to exchange array information internally;
* define here as needed.
*/
#if !defined(PyBUF_SIMPLE)
typedef struct bufferinfo {
void *buf;
PyObject *obj;
Py_ssize_t len;
Py_ssize_t itemsize;
int readonly;
int ndim;
char *format;
Py_ssize_t *shape;
Py_ssize_t *strides;
Py_ssize_t *suboffsets;
void *internal;
} Py_buffer;
/* Flags for getting buffers */
#define PyBUF_SIMPLE 0
#define PyBUF_WRITABLE 0x0001
/* we used to include an E, backwards compatible alias */
#define PyBUF_WRITEABLE PyBUF_WRITABLE
#define PyBUF_FORMAT 0x0004
#define PyBUF_ND 0x0008
#define PyBUF_STRIDES (0x0010 | PyBUF_ND)
#define PyBUF_C_CONTIGUOUS (0x0020 | PyBUF_STRIDES)
#define PyBUF_F_CONTIGUOUS (0x0040 | PyBUF_STRIDES)
#define PyBUF_ANY_CONTIGUOUS (0x0080 | PyBUF_STRIDES)
#define PyBUF_INDIRECT (0x0100 | PyBUF_STRIDES)
#define PyBUF_CONTIG (PyBUF_ND | PyBUF_WRITABLE)
#define PyBUF_CONTIG_RO (PyBUF_ND)
#define PyBUF_STRIDED (PyBUF_STRIDES | PyBUF_WRITABLE)
#define PyBUF_STRIDED_RO (PyBUF_STRIDES)
#define PyBUF_RECORDS (PyBUF_STRIDES | PyBUF_WRITABLE | PyBUF_FORMAT)
#define PyBUF_RECORDS_RO (PyBUF_STRIDES | PyBUF_FORMAT)
#define PyBUF_FULL (PyBUF_INDIRECT | PyBUF_WRITABLE | PyBUF_FORMAT)
#define PyBUF_FULL_RO (PyBUF_INDIRECT | PyBUF_FORMAT)
#define PyBUF_READ 0x100
#define PyBUF_WRITE 0x200
#define PyBUF_SHADOW 0x400
typedef int (*getbufferproc)(PyObject *, Py_buffer *, int);
typedef void (*releasebufferproc)(Py_buffer *);
#endif /* #if !defined(PyBUF_SIMPLE) */
/* Flag indicating a pg_buffer; used for assertions within callbacks */
#ifndef NDEBUG
#define PyBUF_PYGAME 0x4000
#endif
#define PyBUF_HAS_FLAG(f, F) (((f) & (F)) == (F))
/* Array information exchange struct C type; inherits from Py_buffer
*
* Pygame uses its own Py_buffer derived C struct as an internal representation
* of an imported array buffer. The extended Py_buffer allows for a
* per-instance release callback,
*/
typedef void (*pybuffer_releaseproc)(Py_buffer *);
typedef struct pg_bufferinfo_s {
Py_buffer view;
PyObject *consumer; /* Input: Borrowed reference */
pybuffer_releaseproc release_buffer;
} pg_buffer;
/* Operating system specific adjustments
*/
// No signal()
#if defined(__SYMBIAN32__) && defined(HAVE_SIGNAL_H)
#undef HAVE_SIGNAL_H
#endif
#if defined(HAVE_SNPRINTF)
#undef HAVE_SNPRINTF
#endif
#ifdef MS_WIN32 /*Python gives us MS_WIN32, SDL needs just WIN32*/
#ifndef WIN32
#define WIN32
#endif
#endif
/// Prefix when initializing module
#define MODPREFIX ""
/// Prefix when importing module
#define IMPPREFIX "pygame."
#ifdef __SYMBIAN32__
#undef MODPREFIX
#undef IMPPREFIX
// On Symbian there is no pygame package. The extensions are built-in or in
// sys\bin.
#define MODPREFIX "pygame_"
#define IMPPREFIX "pygame_"
#endif
#include <SDL.h>
/* Pygame's SDL version macros:
* IS_SDLv1 is 1 if SDL 1.x.x, 0 otherwise
* IS_SDLv2 is 1 if at least SDL 2.0.0, 0 otherwise
*/
#if (SDL_VERSION_ATLEAST(2, 0, 0))
#define IS_SDLv1 0
#define IS_SDLv2 1
#else
#define IS_SDLv1 1
#define IS_SDLv2 0
#endif
/*#if IS_SDLv1 && PG_MAJOR_VERSION >= 2
#error pygame 2 requires SDL 2
#endif*/
#if IS_SDLv2
/* SDL 1.2 constants removed from SDL 2 */
typedef enum {
SDL_HWSURFACE = 0,
SDL_RESIZABLE = SDL_WINDOW_RESIZABLE,
SDL_ASYNCBLIT = 0,
SDL_OPENGL = SDL_WINDOW_OPENGL,
SDL_OPENGLBLIT = 0,
SDL_ANYFORMAT = 0,
SDL_HWPALETTE = 0,
SDL_DOUBLEBUF = 0,
SDL_FULLSCREEN = SDL_WINDOW_FULLSCREEN,
SDL_HWACCEL = 0,
SDL_SRCCOLORKEY = 0,
SDL_RLEACCELOK = 0,
SDL_SRCALPHA = 0,
SDL_NOFRAME = SDL_WINDOW_BORDERLESS,
SDL_GL_SWAP_CONTROL = 0,
TIMER_RESOLUTION = 0
} PygameVideoFlags;
/* the wheel button constants were removed from SDL 2 */
typedef enum {
PGM_BUTTON_LEFT = SDL_BUTTON_LEFT,
PGM_BUTTON_RIGHT = SDL_BUTTON_RIGHT,
PGM_BUTTON_MIDDLE = SDL_BUTTON_MIDDLE,
PGM_BUTTON_WHEELUP = 4,
PGM_BUTTON_WHEELDOWN = 5,
PGM_BUTTON_X1 = SDL_BUTTON_X1 + 2,
PGM_BUTTON_X2 = SDL_BUTTON_X2 + 2,
PGM_BUTTON_KEEP = 0x80
} PygameMouseFlags;
typedef enum {
SDL_NOEVENT = 0,
/* SDL 1.2 allowed for 8 user defined events. */
SDL_NUMEVENTS = SDL_USEREVENT + 8,
SDL_ACTIVEEVENT = SDL_NUMEVENTS,
PGE_EVENTBEGIN = SDL_NUMEVENTS,
SDL_VIDEORESIZE,
SDL_VIDEOEXPOSE,
PGE_KEYREPEAT,
PGE_EVENTEND
} PygameEventCode;
#define PGE_NUMEVENTS (PGE_EVENTEND - PGE_EVENTBEGIN)
typedef enum {
SDL_APPFOCUSMOUSE,
SDL_APPINPUTFOCUS,
SDL_APPACTIVE
} PygameAppCode;
/* Surface flags: based on SDL 1.2 flags */
typedef enum {
PGS_SWSURFACE = 0x00000000,
PGS_HWSURFACE = 0x00000001,
PGS_ASYNCBLIT = 0x00000004,
PGS_ANYFORMAT = 0x10000000,
PGS_HWPALETTE = 0x20000000,
PGS_DOUBLEBUF = 0x40000000,
PGS_FULLSCREEN = 0x80000000,
PGS_OPENGL = 0x00000002,
PGS_OPENGLBLIT = 0x0000000A,
PGS_RESIZABLE = 0x00000010,
PGS_NOFRAME = 0x00000020,
PGS_SHOWN = 0x00000040, /* Added from SDL 2 */
PGS_HIDDEN = 0x00000080, /* Added from SDL 2 */
PGS_HWACCEL = 0x00000100,
PGS_SRCCOLORKEY = 0x00001000,
PGS_RLEACCELOK = 0x00002000,
PGS_RLEACCEL = 0x00004000,
PGS_SRCALPHA = 0x00010000,
PGS_PREALLOC = 0x01000000
} PygameSurfaceFlags;
typedef struct {
Uint32 hw_available:1;
Uint32 wm_available:1;
Uint32 blit_hw:1;
Uint32 blit_hw_CC:1;
Uint32 blit_hw_A:1;
Uint32 blit_sw:1;
Uint32 blit_sw_CC:1;
Uint32 blit_sw_A:1;
Uint32 blit_fill:1;
Uint32 video_mem;
SDL_PixelFormat *vfmt;
SDL_PixelFormat vfmt_data;
int current_w;
int current_h;
} pg_VideoInfo;
#endif /* IS_SDLv2 */
/* macros used throughout the source */
#define RAISE(x, y) (PyErr_SetString((x), (y)), (PyObject *)NULL)
#ifdef WITH_THREAD
#define PG_CHECK_THREADS() (1)
#else /* ~WITH_THREAD */
#define PG_CHECK_THREADS() \
(RAISE(PyExc_NotImplementedError, \
"Python built without thread support"))
#endif /* ~WITH_THREAD */
#define PyType_Init(x) (((x).ob_type) = &PyType_Type)
#define PYGAMEAPI_LOCAL_ENTRY "_PYGAME_C_API"
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
#ifndef ABS
#define ABS(a) (((a) < 0) ? -(a) : (a))
#endif
/* test sdl initializations */
#define VIDEO_INIT_CHECK() \
if (!SDL_WasInit(SDL_INIT_VIDEO)) \
return RAISE(pgExc_SDLError, "video system not initialized")
#define CDROM_INIT_CHECK() \
if (!SDL_WasInit(SDL_INIT_CDROM)) \
return RAISE(pgExc_SDLError, "cdrom system not initialized")
#define JOYSTICK_INIT_CHECK() \
if (!SDL_WasInit(SDL_INIT_JOYSTICK)) \
return RAISE(pgExc_SDLError, "joystick system not initialized")
/* BASE */
#define VIEW_CONTIGUOUS 1
#define VIEW_C_ORDER 2
#define VIEW_F_ORDER 4
#define PYGAMEAPI_BASE_FIRSTSLOT 0
#if IS_SDLv1
#define PYGAMEAPI_BASE_NUMSLOTS 19
#else /* IS_SDLv2 */
#define PYGAMEAPI_BASE_NUMSLOTS 23
#endif /* IS_SDLv2 */
#ifndef PYGAMEAPI_BASE_INTERNAL
#define pgExc_SDLError ((PyObject *)PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT])
#define pg_RegisterQuit \
(*(void (*)(void (*)(void)))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 1])
#define pg_IntFromObj \
(*(int (*)(PyObject *, int *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 2])
#define pg_IntFromObjIndex \
(*(int (*)(PyObject *, int, \
int *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 3])
#define pg_TwoIntsFromObj \
(*(int (*)(PyObject *, int *, \
int *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 4])
#define pg_FloatFromObj \
(*(int (*)(PyObject *, float *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 5])
#define pg_FloatFromObjIndex \
(*(int (*)(PyObject *, int, \
float *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 6])
#define pg_TwoFloatsFromObj \
(*(int (*)(PyObject *, float *, \
float *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 7])
#define pg_UintFromObj \
(*(int (*)(PyObject *, \
Uint32 *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 8])
#define pg_UintFromObjIndex \
(*(int (*)(PyObject *, int, \
Uint32 *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 9])
#define pgVideo_AutoQuit \
(*(void (*)(void))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 10])
#define pgVideo_AutoInit \
(*(int (*)(void))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 11])
#define pg_RGBAFromObj \
(*(int (*)(PyObject *, \
Uint8 *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 12])
#define pgBuffer_AsArrayInterface \
(*(PyObject * (*)(Py_buffer *)) \
PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 13])
#define pgBuffer_AsArrayStruct \
(*(PyObject * (*)(Py_buffer *)) \
PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 14])
#define pgObject_GetBuffer \
(*(int (*)(PyObject *, pg_buffer *, \
int))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 15])
#define pgBuffer_Release \
(*(void (*)(pg_buffer *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 16])
#define pgDict_AsBuffer \
(*(int (*)(pg_buffer *, PyObject *, \
int))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 17])
#define pgExc_BufferError \
((PyObject *)PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 18])
#if IS_SDLv2
#define pg_GetDefaultWindow \
(*(SDL_Window * (*)(void)) PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 19])
#define pg_SetDefaultWindow \
(*(void (*)(SDL_Window *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 20])
#define pg_GetDefaultWindowSurface \
(*(PyObject * (*)(void)) PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 21])
#define pg_SetDefaultWindowSurface \
(*(void (*)(PyObject *))PyGAME_C_API[PYGAMEAPI_BASE_FIRSTSLOT + 22])
#endif /* IS_SDLv2 */
#define import_pygame_base() IMPORT_PYGAME_MODULE(base, BASE)
#endif
/* RECT */
#define PYGAMEAPI_RECT_FIRSTSLOT \
(PYGAMEAPI_BASE_FIRSTSLOT + PYGAMEAPI_BASE_NUMSLOTS)
#define PYGAMEAPI_RECT_NUMSLOTS 4
#if IS_SDLv1
typedef struct {
int x, y;
int w, h;
} GAME_Rect;
#else
typedef SDL_Rect GAME_Rect;
#endif
typedef struct {
PyObject_HEAD GAME_Rect r;
PyObject *weakreflist;
} pgRectObject;
#define pgRect_AsRect(x) (((pgRectObject *)x)->r)
#ifndef PYGAMEAPI_RECT_INTERNAL
#define pgRect_Check(x) \
((x)->ob_type == \
(PyTypeObject *)PyGAME_C_API[PYGAMEAPI_RECT_FIRSTSLOT + 0])
#define pgRect_Type \
(*(PyTypeObject *)PyGAME_C_API[PYGAMEAPI_RECT_FIRSTSLOT + 0])
#define pgRect_New \
(*(PyObject * (*)(SDL_Rect *)) PyGAME_C_API[PYGAMEAPI_RECT_FIRSTSLOT + 1])
#define pgRect_New4 \
(*(PyObject * (*)(int, int, int, int)) \
PyGAME_C_API[PYGAMEAPI_RECT_FIRSTSLOT + 2])
#define pgRect_FromObject \
(*(GAME_Rect * (*)(PyObject *, GAME_Rect *)) \
PyGAME_C_API[PYGAMEAPI_RECT_FIRSTSLOT + 3])
#define import_pygame_rect() IMPORT_PYGAME_MODULE(rect, RECT)
#endif
/* CDROM */
#define PYGAMEAPI_CDROM_FIRSTSLOT \
(PYGAMEAPI_RECT_FIRSTSLOT + PYGAMEAPI_RECT_NUMSLOTS)
#define PYGAMEAPI_CDROM_NUMSLOTS 2
typedef struct {
PyObject_HEAD int id;
} pgCDObject;
#define pgCD_AsID(x) (((pgCDObject *)x)->id)
#ifndef PYGAMEAPI_CDROM_INTERNAL
#define pgCD_Check(x) \
((x)->ob_type == \
(PyTypeObject *)PyGAME_C_API[PYGAMEAPI_CDROM_FIRSTSLOT + 0])
#define pgCD_Type \
(*(PyTypeObject *)PyGAME_C_API[PYGAMEAPI_CDROM_FIRSTSLOT + 0])
#define pgCD_New \
(*(PyObject * (*)(int)) PyGAME_C_API[PYGAMEAPI_CDROM_FIRSTSLOT + 1])
#define import_pygame_cd() IMPORT_PYGAME_MODULE(cdrom, CDROM)
#endif
/* JOYSTICK */
#define PYGAMEAPI_JOYSTICK_FIRSTSLOT \
(PYGAMEAPI_CDROM_FIRSTSLOT + PYGAMEAPI_CDROM_NUMSLOTS)
#define PYGAMEAPI_JOYSTICK_NUMSLOTS 2
typedef struct {
PyObject_HEAD int id;
} pgJoystickObject;
#define pgJoystick_AsID(x) (((pgJoystickObject *)x)->id)
#ifndef PYGAMEAPI_JOYSTICK_INTERNAL
#define pgJoystick_Check(x) \
((x)->ob_type == \
(PyTypeObject *)PyGAME_C_API[PYGAMEAPI_JOYSTICK_FIRSTSLOT + 0])
#define pgJoystick_Type \
(*(PyTypeObject *)PyGAME_C_API[PYGAMEAPI_JOYSTICK_FIRSTSLOT + 0])
#define pgJoystick_New \
(*(PyObject * (*)(int)) PyGAME_C_API[PYGAMEAPI_JOYSTICK_FIRSTSLOT + 1])
#define import_pygame_joystick() IMPORT_PYGAME_MODULE(joystick, JOYSTICK)
#endif
/* DISPLAY */
#define PYGAMEAPI_DISPLAY_FIRSTSLOT \
(PYGAMEAPI_JOYSTICK_FIRSTSLOT + PYGAMEAPI_JOYSTICK_NUMSLOTS)
#define PYGAMEAPI_DISPLAY_NUMSLOTS 2
typedef struct {
#if IS_SDLv1
PyObject_HEAD SDL_VideoInfo info;
#else
PyObject_HEAD pg_VideoInfo info;
#endif
} pgVidInfoObject;
#define pgVidInfo_AsVidInfo(x) (((pgVidInfoObject *)x)->info)
#ifndef PYGAMEAPI_DISPLAY_INTERNAL
#define pgVidInfo_Check(x) \
((x)->ob_type == \
(PyTypeObject *)PyGAME_C_API[PYGAMEAPI_DISPLAY_FIRSTSLOT + 0])
#define pgVidInfo_Type \
(*(PyTypeObject *)PyGAME_C_API[PYGAMEAPI_DISPLAY_FIRSTSLOT + 0])
#if IS_SDLv1
#define pgVidInfo_New \
(*(PyObject * (*)(SDL_VideoInfo *)) \
PyGAME_C_API[PYGAMEAPI_DISPLAY_FIRSTSLOT + 1])
#else
#define pgVidInfo_New \
(*(PyObject * (*)(pg_VideoInfo *)) \
PyGAME_C_API[PYGAMEAPI_DISPLAY_FIRSTSLOT + 1])
#endif
#define import_pygame_display() IMPORT_PYGAME_MODULE(display, DISPLAY)
#endif
/* SURFACE */
#define PYGAMEAPI_SURFACE_FIRSTSLOT \
(PYGAMEAPI_DISPLAY_FIRSTSLOT + PYGAMEAPI_DISPLAY_NUMSLOTS)
#define PYGAMEAPI_SURFACE_NUMSLOTS 3
typedef struct {
PyObject_HEAD SDL_Surface *surf;
#if IS_SDLv2
int owner;
#endif /* IS_SDLv2 */
struct pgSubSurface_Data *subsurface; /*ptr to subsurface data (if a
* subsurface)*/
PyObject *weakreflist;
PyObject *locklist;
PyObject *dependency;
} pgSurfaceObject;
#define pgSurface_AsSurface(x) (((pgSurfaceObject *)x)->surf)
#ifndef PYGAMEAPI_SURFACE_INTERNAL
#define pgSurface_Check(x) \
(PyObject_IsInstance((x), \
(PyObject *)PyGAME_C_API[PYGAMEAPI_SURFACE_FIRSTSLOT + 0]))
#define pgSurface_Type \
(*(PyTypeObject *)PyGAME_C_API[PYGAMEAPI_SURFACE_FIRSTSLOT + 0])
#if IS_SDLv1
#define pgSurface_New \
(*(PyObject * (*)(SDL_Surface *)) \
PyGAME_C_API[PYGAMEAPI_SURFACE_FIRSTSLOT + 1])
#else /* IS_SDLv2 */
#define pgSurface_New2 \
(*(PyObject * (*)(SDL_Surface *, int)) \
PyGAME_C_API[PYGAMEAPI_SURFACE_FIRSTSLOT + 1])
#endif /* IS_SDLv2 */
#define pgSurface_Blit \
(*(int (*)(PyObject *, PyObject *, SDL_Rect *, SDL_Rect *, \
int))PyGAME_C_API[PYGAMEAPI_SURFACE_FIRSTSLOT + 2])
#define import_pygame_surface() \
do { \
IMPORT_PYGAME_MODULE(surface, SURFACE); \
if (PyErr_Occurred() != NULL) \
break; \
IMPORT_PYGAME_MODULE(surflock, SURFLOCK); \
} while (0)
#if IS_SDLv2
#define pgSurface_New(surface) pgSurface_New2((surface), 1)
#define pgSurface_NewNoOwn(surface) pgSurface_New2((surface), 0)
#endif /* IS_SDLv2 */
#endif
/* SURFLOCK */ /*auto import/init by surface*/
#define PYGAMEAPI_SURFLOCK_FIRSTSLOT \
(PYGAMEAPI_SURFACE_FIRSTSLOT + PYGAMEAPI_SURFACE_NUMSLOTS)
#define PYGAMEAPI_SURFLOCK_NUMSLOTS 8
struct pgSubSurface_Data {
PyObject *owner;
int pixeloffset;
int offsetx, offsety;
};
typedef struct {
PyObject_HEAD PyObject *surface;
PyObject *lockobj;
PyObject *weakrefs;
} pgLifetimeLockObject;
#ifndef PYGAMEAPI_SURFLOCK_INTERNAL
#define pgLifetimeLock_Check(x) \
((x)->ob_type == \
(PyTypeObject *)PyGAME_C_API[PYGAMEAPI_SURFLOCK_FIRSTSLOT + 0])
#define pgSurface_Prep(x) \
if (((pgSurfaceObject *)x)->subsurface) \
(*(*(void (*)( \
PyObject *))PyGAME_C_API[PYGAMEAPI_SURFLOCK_FIRSTSLOT + 1]))(x)
#define pgSurface_Unprep(x) \
if (((pgSurfaceObject *)x)->subsurface) \
(*(*(void (*)( \
PyObject *))PyGAME_C_API[PYGAMEAPI_SURFLOCK_FIRSTSLOT + 2]))(x)
#define pgSurface_Lock \
(*(int (*)(PyObject *))PyGAME_C_API[PYGAMEAPI_SURFLOCK_FIRSTSLOT + 3])
#define pgSurface_Unlock \
(*(int (*)(PyObject *))PyGAME_C_API[PYGAMEAPI_SURFLOCK_FIRSTSLOT + 4])
#define pgSurface_LockBy \
(*(int (*)(PyObject *, \
PyObject *))PyGAME_C_API[PYGAMEAPI_SURFLOCK_FIRSTSLOT + 5])
#define pgSurface_UnlockBy \
(*(int (*)(PyObject *, \
PyObject *))PyGAME_C_API[PYGAMEAPI_SURFLOCK_FIRSTSLOT + 6])
#define pgSurface_LockLifetime \
(*(PyObject * (*)(PyObject *, PyObject *)) \
PyGAME_C_API[PYGAMEAPI_SURFLOCK_FIRSTSLOT + 7])
#endif
/* EVENT */
#define PYGAMEAPI_EVENT_FIRSTSLOT \
(PYGAMEAPI_SURFLOCK_FIRSTSLOT + PYGAMEAPI_SURFLOCK_NUMSLOTS)
#if IS_SDLv1
#define PYGAMEAPI_EVENT_NUMSLOTS 4
#else /* IS_SDLv2 */
#define PYGAMEAPI_EVENT_NUMSLOTS 6
#endif /* IS_SDLv2 */
typedef struct {
PyObject_HEAD int type;
PyObject *dict;
} pgEventObject;
#ifndef PYGAMEAPI_EVENT_INTERNAL
#define pgEvent_Check(x) \
((x)->ob_type == \
(PyTypeObject *)PyGAME_C_API[PYGAMEAPI_EVENT_FIRSTSLOT + 0])
#define pgEvent_Type \
(*(PyTypeObject *)PyGAME_C_API[PYGAMEAPI_EVENT_FIRSTSLOT + 0])
#define pgEvent_New \
(*(PyObject * (*)(SDL_Event *)) \
PyGAME_C_API[PYGAMEAPI_EVENT_FIRSTSLOT + 1])
#define pgEvent_New2 \
(*(PyObject * (*)(int, PyObject *)) \
PyGAME_C_API[PYGAMEAPI_EVENT_FIRSTSLOT + 2])
#define pgEvent_FillUserEvent \
(*(int (*)(pgEventObject *, \
SDL_Event *))PyGAME_C_API[PYGAMEAPI_EVENT_FIRSTSLOT + 3])
#if IS_SDLv2
#define pg_EnableKeyRepeat \
(*(int (*)(int, int))PyGAME_C_API[PYGAMEAPI_EVENT_FIRSTSLOT + 4])
#define pg_GetKeyRepeat \
(*(void (*)(int *, int *))PyGAME_C_API[PYGAMEAPI_EVENT_FIRSTSLOT + 5])
#endif /* IS_SDLv2 */
#define import_pygame_event() IMPORT_PYGAME_MODULE(event, EVENT)
#endif
/* RWOBJECT */
/*the rwobject are only needed for C side work, not accessable from python*/
#define PYGAMEAPI_RWOBJECT_FIRSTSLOT \
(PYGAMEAPI_EVENT_FIRSTSLOT + PYGAMEAPI_EVENT_NUMSLOTS)
#define PYGAMEAPI_RWOBJECT_NUMSLOTS 6
#ifndef PYGAMEAPI_RWOBJECT_INTERNAL
#define pgRWops_FromObject \
(*(SDL_RWops * (*)(PyObject *)) \
PyGAME_C_API[PYGAMEAPI_RWOBJECT_FIRSTSLOT + 0])
#define pgRWops_IsFileObject \
(*(int (*)(SDL_RWops *))PyGAME_C_API[PYGAMEAPI_RWOBJECT_FIRSTSLOT + 1])
#define pg_EncodeFilePath \
(*(PyObject * (*)(PyObject *, PyObject *)) \
PyGAME_C_API[PYGAMEAPI_RWOBJECT_FIRSTSLOT + 2])
#define pg_EncodeString \
(*(PyObject * (*)(PyObject *, const char *, const char *, PyObject *)) \
PyGAME_C_API[PYGAMEAPI_RWOBJECT_FIRSTSLOT + 3])
#define pgRWops_FromFileObject \
(*(SDL_RWops * (*)(PyObject *)) \
PyGAME_C_API[PYGAMEAPI_RWOBJECT_FIRSTSLOT + 4])
#define pgRWops_ReleaseObject \
(*(int (*)(SDL_RWops *)) \
PyGAME_C_API[PYGAMEAPI_RWOBJECT_FIRSTSLOT + 5])
#define import_pygame_rwobject() IMPORT_PYGAME_MODULE(rwobject, RWOBJECT)
#endif
/* PixelArray */
#define PYGAMEAPI_PIXELARRAY_FIRSTSLOT \
(PYGAMEAPI_RWOBJECT_FIRSTSLOT + PYGAMEAPI_RWOBJECT_NUMSLOTS)
#define PYGAMEAPI_PIXELARRAY_NUMSLOTS 2
#ifndef PYGAMEAPI_PIXELARRAY_INTERNAL
#define PyPixelArray_Check(x) \
((x)->ob_type == \
(PyTypeObject *)PyGAME_C_API[PYGAMEAPI_PIXELARRAY_FIRSTSLOT + 0])
#define PyPixelArray_New \
(*(PyObject * (*)) PyGAME_C_API[PYGAMEAPI_PIXELARRAY_FIRSTSLOT + 1])
#define import_pygame_pixelarray() IMPORT_PYGAME_MODULE(pixelarray, PIXELARRAY)
#endif /* PYGAMEAPI_PIXELARRAY_INTERNAL */
/* Color */
#define PYGAMEAPI_COLOR_FIRSTSLOT \
(PYGAMEAPI_PIXELARRAY_FIRSTSLOT + PYGAMEAPI_PIXELARRAY_NUMSLOTS)
#define PYGAMEAPI_COLOR_NUMSLOTS 4
#ifndef PYGAMEAPI_COLOR_INTERNAL
#define pgColor_Check(x) \
((x)->ob_type == \
(PyTypeObject *)PyGAME_C_API[PYGAMEAPI_COLOR_FIRSTSLOT + 0])
#define pgColor_Type (*(PyObject *)PyGAME_C_API[PYGAMEAPI_COLOR_FIRSTSLOT])
#define pgColor_New \
(*(PyObject * (*)(Uint8 *)) PyGAME_C_API[PYGAMEAPI_COLOR_FIRSTSLOT + 1])
#define pgColor_NewLength \
(*(PyObject * (*)(Uint8 *, Uint8)) \
PyGAME_C_API[PYGAMEAPI_COLOR_FIRSTSLOT + 3])
#define pg_RGBAFromColorObj \
(*(int (*)(PyObject *, \
Uint8 *))PyGAME_C_API[PYGAMEAPI_COLOR_FIRSTSLOT + 2])
#define import_pygame_color() IMPORT_PYGAME_MODULE(color, COLOR)
#endif /* PYGAMEAPI_COLOR_INTERNAL */
/* Math */
#define PYGAMEAPI_MATH_FIRSTSLOT \
(PYGAMEAPI_COLOR_FIRSTSLOT + PYGAMEAPI_COLOR_NUMSLOTS)
#define PYGAMEAPI_MATH_NUMSLOTS 2
#ifndef PYGAMEAPI_MATH_INTERNAL
#define pgVector2_Check(x) \
((x)->ob_type == \
(PyTypeObject *)PyGAME_C_API[PYGAMEAPI_MATH_FIRSTSLOT + 0])
#define pgVector3_Check(x) \
((x)->ob_type == \
(PyTypeObject *)PyGAME_C_API[PYGAMEAPI_MATH_FIRSTSLOT + 1])
/*
#define pgVector2_New \
(*(PyObject*(*)) PyGAME_C_API[PYGAMEAPI_MATH_FIRSTSLOT + 1])
*/
#define import_pygame_math() IMPORT_PYGAME_MODULE(math, MATH)
#endif /* PYGAMEAPI_MATH_INTERNAL */
#define PG_CAPSULE_NAME(m) (IMPPREFIX m "." PYGAMEAPI_LOCAL_ENTRY)
#define _IMPORT_PYGAME_MODULE(module, MODULE, api_root) \
{ \
PyObject *_module = PyImport_ImportModule(IMPPREFIX #module); \
\
if (_module != NULL) { \
PyObject *_c_api = \
PyObject_GetAttrString(_module, PYGAMEAPI_LOCAL_ENTRY); \
\
Py_DECREF(_module); \
if (_c_api != NULL && PyCapsule_CheckExact(_c_api)) { \
void **localptr = (void **)PyCapsule_GetPointer( \
_c_api, PG_CAPSULE_NAME(#module)); \
\
if (localptr != NULL) { \
memcpy(api_root + PYGAMEAPI_##MODULE##_FIRSTSLOT, \
localptr, \
sizeof(void **) * PYGAMEAPI_##MODULE##_NUMSLOTS); \
} \
} \
Py_XDECREF(_c_api); \
} \
}
#ifndef NO_PYGAME_C_API
#define IMPORT_PYGAME_MODULE(module, MODULE) \
_IMPORT_PYGAME_MODULE(module, MODULE, PyGAME_C_API)
#define PYGAMEAPI_TOTALSLOTS \
(PYGAMEAPI_MATH_FIRSTSLOT + PYGAMEAPI_MATH_NUMSLOTS)
#ifdef PYGAME_H
void *PyGAME_C_API[PYGAMEAPI_TOTALSLOTS] = {NULL};
#else
extern void *PyGAME_C_API[PYGAMEAPI_TOTALSLOTS];
#endif
#endif
#if PG_HAVE_CAPSULE
#define encapsulate_api(ptr, module) \
PyCapsule_New(ptr, PG_CAPSULE_NAME(module), NULL)
#else
#define encapsulate_api(ptr, module) PyCObject_FromVoidPtr(ptr, NULL)
#endif
#ifndef PG_INLINE
#if defined(__clang__)
#define PG_INLINE __inline__ __attribute__((__unused__))
#elif defined(__GNUC__)
#define PG_INLINE __inline__
#elif defined(_MSC_VER)
#define PG_INLINE __inline
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#define PG_INLINE inline
#else
#define PG_INLINE
#endif
#endif
/*last platform compiler stuff*/
#if defined(macintosh) && defined(__MWERKS__) || defined(__SYMBIAN32__)
#define PYGAME_EXPORT __declspec(export)
#else
#define PYGAME_EXPORT
#endif
#endif /* PYGAME_H */

View File

@ -0,0 +1,31 @@
/*
pygame - Python Game Library
Copyright (C) 2000-2001 Pete Shinners
Copyright (C) 2007 Marcus von Appen
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Pete Shinners
pete@shinners.org
*/
#ifndef _SURFACE_H
#define _SURFACE_H
#include "_pygame.h"
#include "surface.h"
#endif

View File

@ -0,0 +1,146 @@
/*
Bitmask 1.7 - A pixel-perfect collision detection library.
Copyright (C) 2002-2005 Ulf Ekstrom except for the bitcount
function which is copyright (C) Donald W. Gillies, 1992.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef BITMASK_H
#define BITMASK_H
#ifdef __cplusplus
extern "C" {
#endif
#include <limits.h>
/* Define INLINE for different compilers. If your compiler does not
support inlining then there might be a performance hit in
bitmask_overlap_area().
*/
#ifndef INLINE
# ifdef __GNUC__
# define INLINE inline
# else
# ifdef _MSC_VER
# define INLINE __inline
# else
# define INLINE
# endif
# endif
#endif
#define BITMASK_W unsigned long int
#define BITMASK_W_LEN (sizeof(BITMASK_W)*CHAR_BIT)
#define BITMASK_W_MASK (BITMASK_W_LEN - 1)
#define BITMASK_N(n) ((BITMASK_W)1 << (n))
typedef struct bitmask
{
int w,h;
BITMASK_W bits[1];
} bitmask_t;
/* Creates a bitmask of width w and height h, where
w and h must both be greater than or equal to 0.
The mask is automatically cleared when created.
*/
bitmask_t *bitmask_create(int w, int h);
/* Frees all the memory allocated by bitmask_create for m. */
void bitmask_free(bitmask_t *m);
/* Clears all bits in the mask */
void bitmask_clear(bitmask_t *m);
/* Sets all bits in the mask */
void bitmask_fill(bitmask_t *m);
/* Flips all bits in the mask */
void bitmask_invert(bitmask_t *m);
/* Counts the bits in the mask */
unsigned int bitmask_count(bitmask_t *m);
/* Returns nonzero if the bit at (x,y) is set. Coordinates start at
(0,0) */
static INLINE int bitmask_getbit(const bitmask_t *m, int x, int y)
{
return (m->bits[x/BITMASK_W_LEN*m->h + y] & BITMASK_N(x & BITMASK_W_MASK)) != 0;
}
/* Sets the bit at (x,y) */
static INLINE void bitmask_setbit(bitmask_t *m, int x, int y)
{
m->bits[x/BITMASK_W_LEN*m->h + y] |= BITMASK_N(x & BITMASK_W_MASK);
}
/* Clears the bit at (x,y) */
static INLINE void bitmask_clearbit(bitmask_t *m, int x, int y)
{
m->bits[x/BITMASK_W_LEN*m->h + y] &= ~BITMASK_N(x & BITMASK_W_MASK);
}
/* Returns nonzero if the masks overlap with the given offset.
The overlap tests uses the following offsets (which may be negative):
+----+----------..
|A | yoffset
| +-+----------..
+--|B
|xoffset
| |
: :
*/
int bitmask_overlap(const bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset);
/* Like bitmask_overlap(), but will also give a point of intersection.
x and y are given in the coordinates of mask a, and are untouched
if there is no overlap. */
int bitmask_overlap_pos(const bitmask_t *a, const bitmask_t *b,
int xoffset, int yoffset, int *x, int *y);
/* Returns the number of overlapping 'pixels' */
int bitmask_overlap_area(const bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset);
/* Fills a mask with the overlap of two other masks. A bitwise AND. */
void bitmask_overlap_mask (const bitmask_t *a, const bitmask_t *b, bitmask_t *c, int xoffset, int yoffset);
/* Draws mask b onto mask a (bitwise OR). Can be used to compose large
(game background?) mask from several submasks, which may speed up
the testing. */
void bitmask_draw(bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset);
void bitmask_erase(bitmask_t *a, const bitmask_t *b, int xoffset, int yoffset);
/* Return a new scaled bitmask, with dimensions w*h. The quality of the
scaling may not be perfect for all circumstances, but it should
be reasonable. If either w or h is 0 a clear 1x1 mask is returned. */
bitmask_t *bitmask_scale(const bitmask_t *m, int w, int h);
/* Convolve b into a, drawing the output into o, shifted by offset. If offset
* is 0, then the (x,y) bit will be set if and only if
* bitmask_overlap(a, b, x - b->w - 1, y - b->h - 1) returns true.
*
* Modifies bits o[xoffset ... xoffset + a->w + b->w - 1)
* [yoffset ... yoffset + a->h + b->h - 1). */
void bitmask_convolve(const bitmask_t *a, const bitmask_t *b, bitmask_t *o, int xoffset, int yoffset);
#ifdef __cplusplus
} /* End of extern "C" { */
#endif
#endif

View File

@ -0,0 +1,201 @@
/*
pygame - Python Game Library
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "pygame.h"
#include "doc/camera_doc.h"
#if defined(__unix__)
#include <structmember.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <fcntl.h> /* low-level i/o */
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
/* on freebsd there is no asm/types */
#ifdef linux
#include <asm/types.h> /* for videodev2.h */
#endif
#include <linux/videodev2.h>
#elif defined(__APPLE__)
#include <AvailabilityMacros.h>
/* We support OSX 10.6 and below. */
#if __MAC_OS_X_VERSION_MAX_ALLOWED <= 1060
#define PYGAME_MAC_CAMERA_OLD 1
#endif
#endif
#if defined(PYGAME_MAC_CAMERA_OLD)
#include <QuickTime/QuickTime.h>
#include <QuickTime/Movies.h>
#include <QuickTime/ImageCompression.h>
#endif
/* some constants used which are not defined on non-v4l machines. */
#ifndef V4L2_PIX_FMT_RGB24
#define V4L2_PIX_FMT_RGB24 'RGB3'
#endif
#ifndef V4L2_PIX_FMT_RGB444
#define V4L2_PIX_FMT_RGB444 'R444'
#endif
#ifndef V4L2_PIX_FMT_YUYV
#define V4L2_PIX_FMT_YUYV 'YUYV'
#endif
#define CLEAR(x) memset (&(x), 0, sizeof (x))
#define SAT(c) if (c & (~255)) { if (c < 0) c = 0; else c = 255; }
#define SAT2(c) ((c) & (~255) ? ((c) < 0 ? 0 : 255) : (c))
#define DEFAULT_WIDTH 640
#define DEFAULT_HEIGHT 480
#define RGB_OUT 1
#define YUV_OUT 2
#define HSV_OUT 4
#define CAM_V4L 1 /* deprecated. the incomplete support in pygame was removed */
#define CAM_V4L2 2
struct buffer {
void * start;
size_t length;
};
#if defined(__unix__)
typedef struct pgCameraObject {
PyObject_HEAD
char* device_name;
int camera_type;
unsigned long pixelformat;
unsigned int color_out;
struct buffer* buffers;
unsigned int n_buffers;
int width;
int height;
int size;
int hflip;
int vflip;
int brightness;
int fd;
} pgCameraObject;
#elif defined(PYGAME_MAC_CAMERA_OLD)
typedef struct pgCameraObject {
PyObject_HEAD
char* device_name; /* unieke name of the device */
OSType pixelformat;
unsigned int color_out;
SeqGrabComponent component; /* A type used by the Sequence Grabber API */
SGChannel channel; /* Channel of the Sequence Grabber */
GWorldPtr gworld; /* Pointer to the struct that holds the data of the captured image */
Rect boundsRect; /* bounds of the image frame */
long size; /* size of the image in our buffer to draw */
int hflip;
int vflip;
short depth;
struct buffer pixels;
//struct buffer tmp_pixels /* place where the flipped image in temporarly stored if hflip or vflip is true.*/
} pgCameraObject;
#else
/* generic definition.
*/
typedef struct pgCameraObject {
PyObject_HEAD
char* device_name;
int camera_type;
unsigned long pixelformat;
unsigned int color_out;
struct buffer* buffers;
unsigned int n_buffers;
int width;
int height;
int size;
int hflip;
int vflip;
int brightness;
int fd;
} pgCameraObject;
#endif
/* internal functions for colorspace conversion */
void colorspace (SDL_Surface *src, SDL_Surface *dst, int cspace);
void rgb24_to_rgb (const void* src, void* dst, int length, SDL_PixelFormat* format);
void rgb444_to_rgb (const void* src, void* dst, int length, SDL_PixelFormat* format);
void rgb_to_yuv (const void* src, void* dst, int length,
unsigned long source, SDL_PixelFormat* format);
void rgb_to_hsv (const void* src, void* dst, int length,
unsigned long source, SDL_PixelFormat* format);
void yuyv_to_rgb (const void* src, void* dst, int length, SDL_PixelFormat* format);
void yuyv_to_yuv (const void* src, void* dst, int length, SDL_PixelFormat* format);
void uyvy_to_rgb (const void* src, void* dst, int length, SDL_PixelFormat* format);
void uyvy_to_yuv (const void* src, void* dst, int length, SDL_PixelFormat* format);
void sbggr8_to_rgb (const void* src, void* dst, int width, int height,
SDL_PixelFormat* format);
void yuv420_to_rgb (const void* src, void* dst, int width, int height,
SDL_PixelFormat* format);
void yuv420_to_yuv (const void* src, void* dst, int width, int height,
SDL_PixelFormat* format);
#if defined(__unix__)
/* internal functions specific to v4l2 */
char** v4l2_list_cameras (int* num_devices);
int v4l2_get_control (int fd, int id, int *value);
int v4l2_set_control (int fd, int id, int value);
PyObject* v4l2_read_raw (pgCameraObject* self);
int v4l2_xioctl (int fd, int request, void *arg);
int v4l2_process_image (pgCameraObject* self, const void *image,
unsigned int buffer_size, SDL_Surface* surf);
int v4l2_query_buffer (pgCameraObject* self);
int v4l2_read_frame (pgCameraObject* self, SDL_Surface* surf);
int v4l2_stop_capturing (pgCameraObject* self);
int v4l2_start_capturing (pgCameraObject* self);
int v4l2_uninit_device (pgCameraObject* self);
int v4l2_init_mmap (pgCameraObject* self);
int v4l2_init_device (pgCameraObject* self);
int v4l2_close_device (pgCameraObject* self);
int v4l2_open_device (pgCameraObject* self);
#elif defined(PYGAME_MAC_CAMERA_OLD)
/* internal functions specific to mac */
char** mac_list_cameras(int* num_devices);
int mac_open_device (pgCameraObject* self);
int mac_init_device(pgCameraObject* self);
int mac_close_device (pgCameraObject* self);
int mac_start_capturing(pgCameraObject* self);
int mac_stop_capturing (pgCameraObject* self);
int mac_get_control(pgCameraObject* self, int id, int* value);
int mac_set_control(pgCameraObject* self, int id, int value);
PyObject* mac_read_raw(pgCameraObject *self);
int mac_read_frame(pgCameraObject* self, SDL_Surface* surf);
int mac_camera_idle(pgCameraObject* self);
int mac_copy_gworld_to_surface(pgCameraObject* self, SDL_Surface* surf);
void flip_image(const void* image, void* flipped_image, int width, int height,
short depth, int hflip, int vflip);
#endif

View File

@ -0,0 +1,48 @@
#ifndef _FASTEVENTS_H_
#define _FASTEVENTS_H_
/*
NET2 is a threaded, event based, network IO library for SDL.
Copyright (C) 2002 Bob Pendleton
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation; either version 2.1
of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA
If you do not wish to comply with the terms of the LGPL please
contact the author as other terms are available for a fee.
Bob Pendleton
Bob@Pendleton.com
*/
#include "SDL.h"
#ifdef __cplusplus
extern "C" {
#endif
int FE_Init(void); // Initialize FE
void FE_Quit(void); // shutdown FE
void FE_PumpEvents(void); // replacement for SDL_PumpEvents
int FE_PollEvent(SDL_Event *event); // replacement for SDL_PollEvent
int FE_WaitEvent(SDL_Event *event); // replacement for SDL_WaitEvent
int FE_PushEvent(SDL_Event *event); // replacement for SDL_PushEvent
char *FE_GetError(void); // get the last error
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,57 @@
/*
pygame - Python Game Library
Copyright (C) 2000-2001 Pete Shinners
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Pete Shinners
pete@shinners.org
*/
#include <Python.h>
#if defined(HAVE_SNPRINTF) /* also defined in SDL_ttf (SDL.h) */
#undef HAVE_SNPRINTF /* remove GCC macro redefine warning */
#endif
#include <SDL_ttf.h>
/* test font initialization */
#define FONT_INIT_CHECK() \
if(!(*(int*)PyFONT_C_API[2])) \
return RAISE(pgExc_SDLError, "font system not initialized")
#define PYGAMEAPI_FONT_FIRSTSLOT 0
#define PYGAMEAPI_FONT_NUMSLOTS 3
typedef struct {
PyObject_HEAD
TTF_Font* font;
PyObject* weakreflist;
} PyFontObject;
#define PyFont_AsFont(x) (((PyFontObject*)x)->font)
#ifndef PYGAMEAPI_FONT_INTERNAL
#define PyFont_Check(x) ((x)->ob_type == (PyTypeObject*)PyFONT_C_API[0])
#define PyFont_Type (*(PyTypeObject*)PyFONT_C_API[0])
#define PyFont_New (*(PyObject*(*)(TTF_Font*))PyFONT_C_API[1])
/*slot 2 taken by FONT_INIT_CHECK*/
#define import_pygame_font() \
_IMPORT_PYGAME_MODULE(font, FONT, PyFONT_C_API)
static void* PyFONT_C_API[PYGAMEAPI_FONT_NUMSLOTS] = {NULL};
#endif

View File

@ -0,0 +1,137 @@
/*
pygame - Python Game Library
Copyright (C) 2009 Vicent Marti
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _PYGAME_FREETYPE_H_
#define _PYGAME_FREETYPE_H_
#define PGFT_PYGAME1_COMPAT
#define HAVE_PYGAME_SDL_VIDEO
#define HAVE_PYGAME_SDL_RWOPS
#include "pygame.h"
#include "pgcompat.h"
#if PY3
# define IS_PYTHON_3
#endif
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_CACHE_H
#include FT_XFREE86_H
#include FT_TRIGONOMETRY_H
/**********************************************************
* Global module constants
**********************************************************/
/* Render styles */
#define FT_STYLE_NORMAL 0x00
#define FT_STYLE_STRONG 0x01
#define FT_STYLE_OBLIQUE 0x02
#define FT_STYLE_UNDERLINE 0x04
#define FT_STYLE_WIDE 0x08
#define FT_STYLE_DEFAULT 0xFF
/* Bounding box modes */
#define FT_BBOX_EXACT FT_GLYPH_BBOX_SUBPIXELS
#define FT_BBOX_EXACT_GRIDFIT FT_GLYPH_BBOX_GRIDFIT
#define FT_BBOX_PIXEL FT_GLYPH_BBOX_TRUNCATE
#define FT_BBOX_PIXEL_GRIDFIT FT_GLYPH_BBOX_PIXELS
/* Rendering flags */
#define FT_RFLAG_NONE (0)
#define FT_RFLAG_ANTIALIAS (1 << 0)
#define FT_RFLAG_AUTOHINT (1 << 1)
#define FT_RFLAG_VERTICAL (1 << 2)
#define FT_RFLAG_HINTED (1 << 3)
#define FT_RFLAG_KERNING (1 << 4)
#define FT_RFLAG_TRANSFORM (1 << 5)
#define FT_RFLAG_PAD (1 << 6)
#define FT_RFLAG_ORIGIN (1 << 7)
#define FT_RFLAG_UCS4 (1 << 8)
#define FT_RFLAG_USE_BITMAP_STRIKES (1 << 9)
#define FT_RFLAG_DEFAULTS (FT_RFLAG_HINTED | \
FT_RFLAG_USE_BITMAP_STRIKES | \
FT_RFLAG_ANTIALIAS)
#define FT_RENDER_NEWBYTEARRAY 0x0
#define FT_RENDER_NEWSURFACE 0x1
#define FT_RENDER_EXISTINGSURFACE 0x2
/**********************************************************
* Global module types
**********************************************************/
typedef struct _scale_s {
FT_UInt x, y;
} Scale_t;
typedef FT_Angle Angle_t;
struct fontinternals_;
struct freetypeinstance_;
typedef struct {
FT_Long font_index;
FT_Open_Args open_args;
} pgFontId;
typedef struct {
PyObject_HEAD
pgFontId id;
PyObject *path;
int is_scalable;
Scale_t face_size;
FT_Int16 style;
FT_Int16 render_flags;
double strength;
double underline_adjustment;
FT_UInt resolution;
Angle_t rotation;
FT_Matrix transform;
FT_Byte fgcolor[4];
struct freetypeinstance_ *freetype; /* Personal reference */
struct fontinternals_ *_internals;
} pgFontObject;
#define pgFont_IS_ALIVE(o) \
(((pgFontObject *)(o))->_internals != 0)
/**********************************************************
* Module declaration
**********************************************************/
#define PYGAMEAPI_FREETYPE_FIRSTSLOT 0
#define PYGAMEAPI_FREETYPE_NUMSLOTS 2
#ifndef PYGAME_FREETYPE_INTERNAL
#define pgFont_Check(x) ((x)->ob_type == (PyTypeObject*)PgFREETYPE_C_API[0])
#define pgFont_Type (*(PyTypeObject*)PgFREETYPE_C_API[1])
#define pgFont_New (*(PyObject*(*)(const char*, long))PgFREETYPE_C_API[1])
#define import_pygame_freetype() \
_IMPORT_PYGAME_MODULE(freetype, FREETYPE, PgFREETYPE_C_API)
static void *PgFREETYPE_C_API[PYGAMEAPI_FREETYPE_NUMSLOTS] = {0};
#endif /* PYGAME_FREETYPE_INTERNAL */
#endif /* _PYGAME_FREETYPE_H_ */

View File

@ -0,0 +1,25 @@
#include <Python.h>
#include "bitmask.h"
#define PYGAMEAPI_MASK_FIRSTSLOT 0
#define PYGAMEAPI_MASK_NUMSLOTS 1
#define PYGAMEAPI_LOCAL_ENTRY "_PYGAME_C_API"
typedef struct {
PyObject_HEAD
bitmask_t *mask;
} pgMaskObject;
#define pgMask_AsBitmap(x) (((pgMaskObject*)x)->mask)
#ifndef PYGAMEAPI_MASK_INTERNAL
#define pgMask_Type (*(PyTypeObject*)PyMASK_C_API[0])
#define pgMask_Check(x) ((x)->ob_type == &pgMask_Type)
#define import_pygame_mask() \
_IMPORT_PYGAME_MODULE(mask, MASK, PyMASK_C_API)
static void* PyMASK_C_API[PYGAMEAPI_MASK_NUMSLOTS] = {NULL};
#endif /* #ifndef PYGAMEAPI_MASK_INTERNAL */

View File

@ -0,0 +1,65 @@
/*
pygame - Python Game Library
Copyright (C) 2000-2001 Pete Shinners
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Pete Shinners
pete@shinners.org
*/
#include <Python.h>
#include <SDL_mixer.h>
#include <structmember.h>
/* test mixer initializations */
#define MIXER_INIT_CHECK() \
if(!SDL_WasInit(SDL_INIT_AUDIO)) \
return RAISE(pgExc_SDLError, "mixer not initialized")
#define PYGAMEAPI_MIXER_FIRSTSLOT 0
#define PYGAMEAPI_MIXER_NUMSLOTS 7
typedef struct {
PyObject_HEAD
Mix_Chunk *chunk;
Uint8 *mem;
PyObject *weakreflist;
} pgSoundObject;
typedef struct {
PyObject_HEAD
int chan;
} pgChannelObject;
#define pgSound_AsChunk(x) (((pgSoundObject*)x)->chunk)
#define pgChannel_AsInt(x) (((pgChannelObject*)x)->chan)
#ifndef PYGAMEAPI_MIXER_INTERNAL
#define pgSound_Check(x) ((x)->ob_type == (PyTypeObject*)pgMIXER_C_API[0])
#define pgSound_Type (*(PyTypeObject*)pgMIXER_C_API[0])
#define pgSound_New (*(PyObject*(*)(Mix_Chunk*))pgMIXER_C_API[1])
#define pgSound_Play (*(PyObject*(*)(PyObject*, PyObject*))pgMIXER_C_API[2])
#define pgChannel_Check(x) ((x)->ob_type == (PyTypeObject*)pgMIXER_C_API[3])
#define pgChannel_Type (*(PyTypeObject*)pgMIXER_C_API[3])
#define pgChannel_New (*(PyObject*(*)(int))pgMIXER_C_API[4])
#define pgMixer_AutoInit (*(PyObject*(*)(PyObject*, PyObject*))pgMIXER_C_API[5])
#define pgMixer_AutoQuit (*(void(*)(void))pgMIXER_C_API[6])
#define import_pygame_mixer() \
_IMPORT_PYGAME_MODULE(mixer, MIXER, pgMIXER_C_API)
static void* pgMIXER_C_API[PYGAMEAPI_MIXER_NUMSLOTS] = {NULL};
#endif

View File

@ -0,0 +1,123 @@
/*
pygame - Python Game Library
Copyright (C) 2000-2001 Pete Shinners
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Pete Shinners
pete@shinners.org
*/
#ifndef PALETTE_H
#define PALETTE_H
#include <SDL.h>
/* SDL 2 does not assign a default palette color scheme to a new 8 bit
* surface. Instead, the palette is set all white. This defines the SDL 1.2
* default palette.
*/
static const SDL_Color default_palette_colors[] = {
{0, 0, 0, 255}, {0, 0, 85, 255}, {0, 0, 170, 255},
{0, 0, 255, 255}, {0, 36, 0, 255}, {0, 36, 85, 255},
{0, 36, 170, 255}, {0, 36, 255, 255}, {0, 73, 0, 255},
{0, 73, 85, 255}, {0, 73, 170, 255}, {0, 73, 255, 255},
{0, 109, 0, 255}, {0, 109, 85, 255}, {0, 109, 170, 255},
{0, 109, 255, 255}, {0, 146, 0, 255}, {0, 146, 85, 255},
{0, 146, 170, 255}, {0, 146, 255, 255}, {0, 182, 0, 255},
{0, 182, 85, 255}, {0, 182, 170, 255}, {0, 182, 255, 255},
{0, 219, 0, 255}, {0, 219, 85, 255}, {0, 219, 170, 255},
{0, 219, 255, 255}, {0, 255, 0, 255}, {0, 255, 85, 255},
{0, 255, 170, 255}, {0, 255, 255, 255}, {85, 0, 0, 255},
{85, 0, 85, 255}, {85, 0, 170, 255}, {85, 0, 255, 255},
{85, 36, 0, 255}, {85, 36, 85, 255}, {85, 36, 170, 255},
{85, 36, 255, 255}, {85, 73, 0, 255}, {85, 73, 85, 255},
{85, 73, 170, 255}, {85, 73, 255, 255}, {85, 109, 0, 255},
{85, 109, 85, 255}, {85, 109, 170, 255}, {85, 109, 255, 255},
{85, 146, 0, 255}, {85, 146, 85, 255}, {85, 146, 170, 255},
{85, 146, 255, 255}, {85, 182, 0, 255}, {85, 182, 85, 255},
{85, 182, 170, 255}, {85, 182, 255, 255}, {85, 219, 0, 255},
{85, 219, 85, 255}, {85, 219, 170, 255}, {85, 219, 255, 255},
{85, 255, 0, 255}, {85, 255, 85, 255}, {85, 255, 170, 255},
{85, 255, 255, 255}, {170, 0, 0, 255}, {170, 0, 85, 255},
{170, 0, 170, 255}, {170, 0, 255, 255}, {170, 36, 0, 255},
{170, 36, 85, 255}, {170, 36, 170, 255}, {170, 36, 255, 255},
{170, 73, 0, 255}, {170, 73, 85, 255}, {170, 73, 170, 255},
{170, 73, 255, 255}, {170, 109, 0, 255}, {170, 109, 85, 255},
{170, 109, 170, 255}, {170, 109, 255, 255}, {170, 146, 0, 255},
{170, 146, 85, 255}, {170, 146, 170, 255}, {170, 146, 255, 255},
{170, 182, 0, 255}, {170, 182, 85, 255}, {170, 182, 170, 255},
{170, 182, 255, 255}, {170, 219, 0, 255}, {170, 219, 85, 255},
{170, 219, 170, 255}, {170, 219, 255, 255}, {170, 255, 0, 255},
{170, 255, 85, 255}, {170, 255, 170, 255}, {170, 255, 255, 255},
{255, 0, 0, 255}, {255, 0, 85, 255}, {255, 0, 170, 255},
{255, 0, 255, 255}, {255, 36, 0, 255}, {255, 36, 85, 255},
{255, 36, 170, 255}, {255, 36, 255, 255}, {255, 73, 0, 255},
{255, 73, 85, 255}, {255, 73, 170, 255}, {255, 73, 255, 255},
{255, 109, 0, 255}, {255, 109, 85, 255}, {255, 109, 170, 255},
{255, 109, 255, 255}, {255, 146, 0, 255}, {255, 146, 85, 255},
{255, 146, 170, 255}, {255, 146, 255, 255}, {255, 182, 0, 255},
{255, 182, 85, 255}, {255, 182, 170, 255}, {255, 182, 255, 255},
{255, 219, 0, 255}, {255, 219, 85, 255}, {255, 219, 170, 255},
{255, 219, 255, 255}, {255, 255, 0, 255}, {255, 255, 85, 255},
{255, 255, 170, 255}, {255, 255, 255, 255}, {0, 0, 0, 255},
{0, 0, 85, 255}, {0, 0, 170, 255}, {0, 0, 255, 255},
{0, 36, 0, 255}, {0, 36, 85, 255}, {0, 36, 170, 255},
{0, 36, 255, 255}, {0, 73, 0, 255}, {0, 73, 85, 255},
{0, 73, 170, 255}, {0, 73, 255, 255}, {0, 109, 0, 255},
{0, 109, 85, 255}, {0, 109, 170, 255}, {0, 109, 255, 255},
{0, 146, 0, 255}, {0, 146, 85, 255}, {0, 146, 170, 255},
{0, 146, 255, 255}, {0, 182, 0, 255}, {0, 182, 85, 255},
{0, 182, 170, 255}, {0, 182, 255, 255}, {0, 219, 0, 255},
{0, 219, 85, 255}, {0, 219, 170, 255}, {0, 219, 255, 255},
{0, 255, 0, 255}, {0, 255, 85, 255}, {0, 255, 170, 255},
{0, 255, 255, 255}, {85, 0, 0, 255}, {85, 0, 85, 255},
{85, 0, 170, 255}, {85, 0, 255, 255}, {85, 36, 0, 255},
{85, 36, 85, 255}, {85, 36, 170, 255}, {85, 36, 255, 255},
{85, 73, 0, 255}, {85, 73, 85, 255}, {85, 73, 170, 255},
{85, 73, 255, 255}, {85, 109, 0, 255}, {85, 109, 85, 255},
{85, 109, 170, 255}, {85, 109, 255, 255}, {85, 146, 0, 255},
{85, 146, 85, 255}, {85, 146, 170, 255}, {85, 146, 255, 255},
{85, 182, 0, 255}, {85, 182, 85, 255}, {85, 182, 170, 255},
{85, 182, 255, 255}, {85, 219, 0, 255}, {85, 219, 85, 255},
{85, 219, 170, 255}, {85, 219, 255, 255}, {85, 255, 0, 255},
{85, 255, 85, 255}, {85, 255, 170, 255}, {85, 255, 255, 255},
{170, 0, 0, 255}, {170, 0, 85, 255}, {170, 0, 170, 255},
{170, 0, 255, 255}, {170, 36, 0, 255}, {170, 36, 85, 255},
{170, 36, 170, 255}, {170, 36, 255, 255}, {170, 73, 0, 255},
{170, 73, 85, 255}, {170, 73, 170, 255}, {170, 73, 255, 255},
{170, 109, 0, 255}, {170, 109, 85, 255}, {170, 109, 170, 255},
{170, 109, 255, 255}, {170, 146, 0, 255}, {170, 146, 85, 255},
{170, 146, 170, 255}, {170, 146, 255, 255}, {170, 182, 0, 255},
{170, 182, 85, 255}, {170, 182, 170, 255}, {170, 182, 255, 255},
{170, 219, 0, 255}, {170, 219, 85, 255}, {170, 219, 170, 255},
{170, 219, 255, 255}, {170, 255, 0, 255}, {170, 255, 85, 255},
{170, 255, 170, 255}, {170, 255, 255, 255}, {255, 0, 0, 255},
{255, 0, 85, 255}, {255, 0, 170, 255}, {255, 0, 255, 255},
{255, 36, 0, 255}, {255, 36, 85, 255}, {255, 36, 170, 255},
{255, 36, 255, 255}, {255, 73, 0, 255}, {255, 73, 85, 255},
{255, 73, 170, 255}, {255, 73, 255, 255}, {255, 109, 0, 255},
{255, 109, 85, 255}, {255, 109, 170, 255}, {255, 109, 255, 255},
{255, 146, 0, 255}, {255, 146, 85, 255}, {255, 146, 170, 255},
{255, 146, 255, 255}, {255, 182, 0, 255}, {255, 182, 85, 255},
{255, 182, 170, 255}, {255, 182, 255, 255}, {255, 219, 0, 255},
{255, 219, 85, 255}, {255, 219, 170, 255}, {255, 219, 255, 255},
{255, 255, 0, 255}, {255, 255, 85, 255}, {255, 255, 170, 255},
{255, 255, 255, 255}};
static const int default_palette_size =
(int)(sizeof(default_palette_colors) / sizeof(SDL_Color));
#endif

View File

@ -0,0 +1,26 @@
/* array structure interface version 3 declarations */
#if !defined(PG_ARRAYINTER_HEADER)
#define PG_ARRAYINTER_HEADER
static const int PAI_CONTIGUOUS = 0x01;
static const int PAI_FORTRAN = 0x02;
static const int PAI_ALIGNED = 0x100;
static const int PAI_NOTSWAPPED = 0x200;
static const int PAI_WRITEABLE = 0x400;
static const int PAI_ARR_HAS_DESCR = 0x800;
typedef struct {
int two; /* contains the integer 2 -- simple sanity check */
int nd; /* number of dimensions */
char typekind; /* kind in array -- character code of typestr */
int itemsize; /* size of each element */
int flags; /* flags indicating how the data should be */
/* interpreted */
Py_intptr_t *shape; /* A length-nd array of shape information */
Py_intptr_t *strides; /* A length-nd array of stride information */
void *data; /* A pointer to the first element of the array */
PyObject *descr; /* NULL or a data-description */
} PyArrayInterface;
#endif

View File

@ -0,0 +1,52 @@
/*
pygame - Python Game Library
Copyright (C) 2000-2001 Pete Shinners
Copyright (C) 2007 Rene Dudfield, Richard Goedeken
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Pete Shinners
pete@shinners.org
*/
/* Bufferproxy module C api.
Depends on pygame.h being included first.
*/
#if !defined(PG_BUFPROXY_HEADER)
#define PYGAMEAPI_BUFPROXY_NUMSLOTS 4
#define PYGAMEAPI_BUFPROXY_FIRSTSLOT 0
#if !(defined(PYGAMEAPI_BUFPROXY_INTERNAL) || defined(NO_PYGAME_C_API))
static void *PgBUFPROXY_C_API[PYGAMEAPI_BUFPROXY_NUMSLOTS];
typedef PyObject *(*_pgbufproxy_new_t)(PyObject *, getbufferproc);
typedef PyObject *(*_pgbufproxy_get_obj_t)(PyObject *);
typedef int (*_pgbufproxy_trip_t)(PyObject *);
#define pgBufproxy_Type (*(PyTypeObject*)PgBUFPROXY_C_API[0])
#define pgBufproxy_New (*(_pgbufproxy_new_t)PgBUFPROXY_C_API[1])
#define pgBufproxy_GetParent \
(*(_pgbufproxy_get_obj_t)PgBUFPROXY_C_API[2])
#define pgBufproxy_Trip (*(_pgbufproxy_trip_t)PgBUFPROXY_C_API[3])
#define pgBufproxy_Check(x) ((x)->ob_type == (pgBufproxy_Type))
#define import_pygame_bufferproxy() \
_IMPORT_PYGAME_MODULE(bufferproxy, BUFPROXY, PgBUFPROXY_C_API)
#endif /* #if !(defined(PYGAMEAPI_BUFPROXY_INTERNAL) || ... */
#define PG_BUFPROXY_HEADER
#endif /* #if !defined(PG_BUFPROXY_HEADER) */

View File

@ -0,0 +1,195 @@
/* Python 2.x/3.x compitibility tools
*/
#if !defined(PGCOMPAT_H)
#define PGCOMPAT_H
#if PY_MAJOR_VERSION >= 3
#define PY3 1
/* Define some aliases for the removed PyInt_* functions */
#define PyInt_Check(op) PyLong_Check(op)
#define PyInt_FromString PyLong_FromString
#define PyInt_FromUnicode PyLong_FromUnicode
#define PyInt_FromLong PyLong_FromLong
#define PyInt_FromSize_t PyLong_FromSize_t
#define PyInt_FromSsize_t PyLong_FromSsize_t
#define PyInt_AsLong PyLong_AsLong
#define PyInt_AsSsize_t PyLong_AsSsize_t
#define PyInt_AsUnsignedLongMask PyLong_AsUnsignedLongMask
#define PyInt_AsUnsignedLongLongMask PyLong_AsUnsignedLongLongMask
#define PyInt_AS_LONG PyLong_AS_LONG
#define PyNumber_Int PyNumber_Long
/* Weakrefs flags changed in 3.x */
#define Py_TPFLAGS_HAVE_WEAKREFS 0
/* Module init function returns new module instance. */
#define MODINIT_RETURN(x) return x
#define MODINIT_DEFINE(mod_name) PyMODINIT_FUNC PyInit_##mod_name (void)
#define DECREF_MOD(mod) Py_DECREF (mod)
/* Type header differs. */
#define TYPE_HEAD(x,y) PyVarObject_HEAD_INIT(x,y)
/* Text interface. Use unicode strings. */
#define Text_Type PyUnicode_Type
#define Text_Check PyUnicode_Check
#ifndef PYPY_VERSION
#define Text_FromLocale(s) PyUnicode_DecodeLocale((s), "strict")
#else /* PYPY_VERSION */
/* workaround: missing function for pypy */
#define Text_FromLocale PyUnicode_FromString
#endif /* PYPY_VERSION */
#define Text_FromUTF8 PyUnicode_FromString
#define Text_FromUTF8AndSize PyUnicode_FromStringAndSize
#define Text_FromFormat PyUnicode_FromFormat
#define Text_GetSize PyUnicode_GetSize
#define Text_GET_SIZE PyUnicode_GET_SIZE
/* Binary interface. Use bytes. */
#define Bytes_Type PyBytes_Type
#define Bytes_Check PyBytes_Check
#define Bytes_Size PyBytes_Size
#define Bytes_AsString PyBytes_AsString
#define Bytes_AsStringAndSize PyBytes_AsStringAndSize
#define Bytes_FromStringAndSize PyBytes_FromStringAndSize
#define Bytes_FromFormat PyBytes_FromFormat
#define Bytes_AS_STRING PyBytes_AS_STRING
#define Bytes_GET_SIZE PyBytes_GET_SIZE
#define Bytes_AsDecodeObject PyBytes_AsDecodedObject
#define Object_Unicode PyObject_Str
#define IsTextObj(x) (PyUnicode_Check(x) || PyBytes_Check(x))
/* Renamed builtins */
#define BUILTINS_MODULE "builtins"
#define BUILTINS_UNICODE "str"
#define BUILTINS_UNICHR "chr"
/* Defaults for unicode file path encoding */
#define UNICODE_DEF_FS_CODEC Py_FileSystemDefaultEncoding
#if defined(MS_WIN32)
#define UNICODE_DEF_FS_ERROR "replace"
#else
#define UNICODE_DEF_FS_ERROR "surrogateescape"
#endif
#else /* #if PY_MAJOR_VERSION >= 3 */
#define PY3 0
/* Module init function returns nothing. */
#define MODINIT_RETURN(x) return
#define MODINIT_DEFINE(mod_name) PyMODINIT_FUNC init##mod_name (void)
#define DECREF_MOD(mod)
/* Type header differs. */
#define TYPE_HEAD(x,y) \
PyObject_HEAD_INIT(x) \
0,
/* Text interface. Use ascii strings. */
#define Text_Type PyString_Type
#define Text_Check PyString_Check
#define Text_FromLocale PyString_FromString
#define Text_FromUTF8 PyString_FromString
#define Text_FromUTF8AndSize PyString_FromStringAndSize
#define Text_FromFormat PyString_FromFormat
#define Text_GetSize PyString_GetSize
#define Text_GET_SIZE PyString_GET_SIZE
/* Binary interface. Use ascii strings. */
#define Bytes_Type PyString_Type
#define Bytes_Check PyString_Check
#define Bytes_Size PyString_Size
#define Bytes_AsString PyString_AsString
#define Bytes_AsStringAndSize PyString_AsStringAndSize
#define Bytes_FromStringAndSize PyString_FromStringAndSize
#define Bytes_FromFormat PyString_FromFormat
#define Bytes_AS_STRING PyString_AS_STRING
#define Bytes_GET_SIZE PyString_GET_SIZE
#define Bytes_AsDecodedObject PyString_AsDecodedObject
#define Object_Unicode PyObject_Unicode
/* Renamed builtins */
#define BUILTINS_MODULE "__builtin__"
#define BUILTINS_UNICODE "unicode"
#define BUILTINS_UNICHR "unichr"
/* Defaults for unicode file path encoding */
#define UNICODE_DEF_FS_CODEC Py_FileSystemDefaultEncoding
#define UNICODE_DEF_FS_ERROR "strict"
#endif /* #if PY_MAJOR_VERSION >= 3 */
#define PY2 (!PY3)
#define MODINIT_ERROR MODINIT_RETURN (NULL)
/* Module state. These macros are used to define per-module macros.
* v - global state variable (Python 2.x)
* s - global state structure (Python 3.x)
*/
#define PY2_GETSTATE(v) (&(v))
#define PY3_GETSTATE(s, m) ((struct s *) PyModule_GetState (m))
/* Pep 3123: Making PyObject_HEAD conform to standard C */
#if !defined(Py_TYPE)
#define Py_TYPE(o) (((PyObject *)(o))->ob_type)
#define Py_REFCNT(o) (((PyObject *)(o))->ob_refcnt)
#define Py_SIZE(o) (((PyVarObject *)(o))->ob_size)
#endif
/* Encode a unicode file path */
#define Unicode_AsEncodedPath(u) \
PyUnicode_AsEncodedString ((u), UNICODE_DEF_FS_CODEC, UNICODE_DEF_FS_ERROR)
#define RELATIVE_MODULE(m) ("." m)
#define HAVE_OLD_BUFPROTO PY2
#if !defined(PG_ENABLE_OLDBUF) /* allow for command line override */
#if HAVE_OLD_BUFPROTO
#define PG_ENABLE_OLDBUF 1
#else
#define PG_ENABLE_OLDBUF 0
#endif
#endif
#ifndef Py_TPFLAGS_HAVE_NEWBUFFER
#define Py_TPFLAGS_HAVE_NEWBUFFER 0
#endif
#ifndef Py_TPFLAGS_HAVE_CLASS
#define Py_TPFLAGS_HAVE_CLASS 0
#endif
#ifndef Py_TPFLAGS_CHECKTYPES
#define Py_TPFLAGS_CHECKTYPES 0
#endif
#if PY_VERSION_HEX >= 0x03020000
#define Slice_GET_INDICES_EX(slice, length, start, stop, step, slicelength) \
PySlice_GetIndicesEx(slice, length, start, stop, step, slicelength)
#else
#define Slice_GET_INDICES_EX(slice, length, start, stop, step, slicelength) \
PySlice_GetIndicesEx((PySliceObject *)(slice), length, \
start, stop, step, slicelength)
#endif
/* Support new buffer protocol? */
#if !defined(PG_ENABLE_NEWBUF) /* allow for command line override */
#if !defined(PYPY_VERSION)
#define PG_ENABLE_NEWBUF 1
#else
#define PG_ENABLE_NEWBUF 0
#endif
#endif
#endif /* #if !defined(PGCOMPAT_H) */

View File

@ -0,0 +1,16 @@
#if !defined(PGOPENGL_H)
#define PGOPENGL_H
/** This header includes definitions of Opengl functions as pointer types for
** use with the SDL function SDL_GL_GetProcAddress.
**/
#if defined(_WIN32)
#define GL_APIENTRY __stdcall
#else
#define GL_APIENTRY
#endif
typedef void (GL_APIENTRY *GL_glReadPixels_Func)(int, int, int, int, unsigned int, unsigned int, void*);
#endif

View File

@ -0,0 +1,34 @@
/*
pygame - Python Game Library
Copyright (C) 2000-2001 Pete Shinners
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Pete Shinners
pete@shinners.org
*/
/* To allow the Pygame C api to be globally shared by all code within an
* extension module built from multiple C files, only include the pygame.h
* header within the top level C file, the one which calls the
* 'import_pygame_*' macros. All other C source files of the module should
* include _pygame.h instead.
*/
#ifndef PYGAME_H
#define PYGAME_H
#include "_pygame.h"
#endif

View File

@ -0,0 +1,143 @@
/*
pygame - Python Game Library
Copyright (C) 2006, 2007 Rene Dudfield, Marcus von Appen
Originally put in the public domain by Sam Lantinga.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* This is unconditionally defined in Python.h */
#if defined(_POSIX_C_SOURCE)
#undef _POSIX_C_SOURCE
#endif
#include <Python.h>
/* Handle clipboard text and data in arbitrary formats */
/**
* Predefined supported pygame scrap types.
*/
#define PYGAME_SCRAP_TEXT "text/plain"
#define PYGAME_SCRAP_BMP "image/bmp"
#define PYGAME_SCRAP_PPM "image/ppm"
#define PYGAME_SCRAP_PBM "image/pbm"
/**
* The supported scrap clipboard types.
*
* This is only relevant in a X11 environment, which supports mouse
* selections as well. For Win32 and MacOS environments the default
* clipboard is used, no matter what value is passed.
*/
typedef enum
{
SCRAP_CLIPBOARD,
SCRAP_SELECTION /* only supported in X11 environments. */
} ScrapClipType;
/**
* Macro for initialization checks.
*/
#define PYGAME_SCRAP_INIT_CHECK() \
if(!pygame_scrap_initialized()) \
return (PyErr_SetString (pgExc_SDLError, \
"scrap system not initialized."), NULL)
/**
* \brief Checks, whether the pygame scrap module was initialized.
*
* \return 1 if the modules was initialized, 0 otherwise.
*/
extern int
pygame_scrap_initialized (void);
/**
* \brief Initializes the pygame scrap module internals. Call this before any
* other method.
*
* \return 1 on successful initialization, 0 otherwise.
*/
extern int
pygame_scrap_init (void);
/**
* \brief Checks, whether the pygame window lost the clipboard focus or not.
*
* \return 1 if the window lost the focus, 0 otherwise.
*/
extern int
pygame_scrap_lost (void);
/**
* \brief Places content of a specific type into the clipboard.
*
* \note For X11 the following notes are important: The following types
* are reserved for internal usage and thus will throw an error on
* setting them: "TIMESTAMP", "TARGETS", "SDL_SELECTION".
* Setting PYGAME_SCRAP_TEXT ("text/plain") will also automatically
* set the X11 types "STRING" (XA_STRING), "TEXT" and "UTF8_STRING".
*
* For Win32 the following notes are important: Setting
* PYGAME_SCRAP_TEXT ("text/plain") will also automatically set
* the Win32 type "TEXT" (CF_TEXT).
*
* For QNX the following notes are important: Setting
* PYGAME_SCRAP_TEXT ("text/plain") will also automatically set
* the QNX type "TEXT" (Ph_CL_TEXT).
*
* \param type The type of the content.
* \param srclen The length of the content.
* \param src The NULL terminated content.
* \return 1, if the content could be successfully pasted into the clipboard,
* 0 otherwise.
*/
extern int
pygame_scrap_put (char *type, int srclen, char *src);
/**
* \brief Gets the current content from the clipboard.
*
* \note The received content does not need to be the content previously
* placed in the clipboard using pygame_put_scrap(). See the
* pygame_put_scrap() notes for more details.
*
* \param type The type of the content to receive.
* \param count The size of the returned content.
* \return The content or NULL in case of an error or if no content of the
* specified type was available.
*/
extern char*
pygame_scrap_get (char *type, unsigned long *count);
/**
* \brief Gets the currently available content types from the clipboard.
*
* \return The different available content types or NULL in case of an
* error or if no content type is available.
*/
extern char**
pygame_scrap_get_types (void);
/**
* \brief Checks whether content for the specified scrap type is currently
* available in the clipboard.
*
* \param type The type to check for.
* \return 1, if there is content and 0 otherwise.
*/
extern int
pygame_scrap_contains (char *type);

View File

@ -0,0 +1,383 @@
/*
pygame - Python Game Library
Copyright (C) 2000-2001 Pete Shinners
Copyright (C) 2007 Marcus von Appen
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Pete Shinners
pete@shinners.org
*/
#ifndef SURFACE_H
#define SURFACE_H
/* This is defined in SDL.h */
#if defined(_POSIX_C_SOURCE)
#undef _POSIX_C_SOURCE
#endif
#include <SDL.h>
#include "pygame.h"
/* Blend modes */
#define PYGAME_BLEND_ADD 0x1
#define PYGAME_BLEND_SUB 0x2
#define PYGAME_BLEND_MULT 0x3
#define PYGAME_BLEND_MIN 0x4
#define PYGAME_BLEND_MAX 0x5
#define PYGAME_BLEND_RGB_ADD 0x1
#define PYGAME_BLEND_RGB_SUB 0x2
#define PYGAME_BLEND_RGB_MULT 0x3
#define PYGAME_BLEND_RGB_MIN 0x4
#define PYGAME_BLEND_RGB_MAX 0x5
#define PYGAME_BLEND_RGBA_ADD 0x6
#define PYGAME_BLEND_RGBA_SUB 0x7
#define PYGAME_BLEND_RGBA_MULT 0x8
#define PYGAME_BLEND_RGBA_MIN 0x9
#define PYGAME_BLEND_RGBA_MAX 0x10
#define PYGAME_BLEND_PREMULTIPLIED 0x11
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
#define GET_PIXEL_24(b) (b[0] + (b[1] << 8) + (b[2] << 16))
#else
#define GET_PIXEL_24(b) (b[2] + (b[1] << 8) + (b[0] << 16))
#endif
#define GET_PIXEL(pxl, bpp, source) \
switch (bpp) \
{ \
case 2: \
pxl = *((Uint16 *) (source)); \
break; \
case 4: \
pxl = *((Uint32 *) (source)); \
break; \
default: \
{ \
Uint8 *b = (Uint8 *) source; \
pxl = GET_PIXEL_24(b); \
} \
break; \
}
#if IS_SDLv1
#define GET_PIXELVALS(_sR, _sG, _sB, _sA, px, fmt, ppa) \
_sR = ((px & fmt->Rmask) >> fmt->Rshift); \
_sR = (_sR << fmt->Rloss) + (_sR >> (8 - (fmt->Rloss << 1))); \
_sG = ((px & fmt->Gmask) >> fmt->Gshift); \
_sG = (_sG << fmt->Gloss) + (_sG >> (8 - (fmt->Gloss << 1))); \
_sB = ((px & fmt->Bmask) >> fmt->Bshift); \
_sB = (_sB << fmt->Bloss) + (_sB >> (8 - (fmt->Bloss << 1))); \
if (ppa) \
{ \
_sA = ((px & fmt->Amask) >> fmt->Ashift); \
_sA = (_sA << fmt->Aloss) + (_sA >> (8 - (fmt->Aloss << 1))); \
} \
else \
{ \
_sA = 255; \
}
#define GET_PIXELVALS_1(sr, sg, sb, sa, _src, _fmt) \
sr = _fmt->palette->colors[*((Uint8 *) (_src))].r; \
sg = _fmt->palette->colors[*((Uint8 *) (_src))].g; \
sb = _fmt->palette->colors[*((Uint8 *) (_src))].b; \
sa = 255;
/* For 1 byte palette pixels */
#define SET_PIXELVAL(px, fmt, _dR, _dG, _dB, _dA) \
*(px) = (Uint8) SDL_MapRGB(fmt, _dR, _dG, _dB)
#else /* IS_SDLv2 */
#define GET_PIXELVALS(_sR, _sG, _sB, _sA, px, fmt, ppa) \
SDL_GetRGBA(px, fmt, &(_sR), &(_sG), &(_sB), &(_sA)); \
if (!ppa) { \
_sA = 255; \
}
#define GET_PIXELVALS_1(sr, sg, sb, sa, _src, _fmt) \
sr = _fmt->palette->colors[*((Uint8 *) (_src))].r; \
sg = _fmt->palette->colors[*((Uint8 *) (_src))].g; \
sb = _fmt->palette->colors[*((Uint8 *) (_src))].b; \
sa = 255;
/* For 1 byte palette pixels */
#define SET_PIXELVAL(px, fmt, _dR, _dG, _dB, _dA) \
*(px) = (Uint8) SDL_MapRGBA(fmt, _dR, _dG, _dB, _dA)
#endif /* IS_SDLv2 */
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
#define SET_OFFSETS_24(or, og, ob, fmt) \
{ \
or = (fmt->Rshift == 0 ? 0 : \
fmt->Rshift == 8 ? 1 : \
2 ); \
og = (fmt->Gshift == 0 ? 0 : \
fmt->Gshift == 8 ? 1 : \
2 ); \
ob = (fmt->Bshift == 0 ? 0 : \
fmt->Bshift == 8 ? 1 : \
2 ); \
}
#define SET_OFFSETS_32(or, og, ob, fmt) \
{ \
or = (fmt->Rshift == 0 ? 0 : \
fmt->Rshift == 8 ? 1 : \
fmt->Rshift == 16 ? 2 : \
3 ); \
og = (fmt->Gshift == 0 ? 0 : \
fmt->Gshift == 8 ? 1 : \
fmt->Gshift == 16 ? 2 : \
3 ); \
ob = (fmt->Bshift == 0 ? 0 : \
fmt->Bshift == 8 ? 1 : \
fmt->Bshift == 16 ? 2 : \
3 ); \
}
#else
#define SET_OFFSETS_24(or, og, ob, fmt) \
{ \
or = (fmt->Rshift == 0 ? 2 : \
fmt->Rshift == 8 ? 1 : \
0 ); \
og = (fmt->Gshift == 0 ? 2 : \
fmt->Gshift == 8 ? 1 : \
0 ); \
ob = (fmt->Bshift == 0 ? 2 : \
fmt->Bshift == 8 ? 1 : \
0 ); \
}
#define SET_OFFSETS_32(or, og, ob, fmt) \
{ \
or = (fmt->Rshift == 0 ? 3 : \
fmt->Rshift == 8 ? 2 : \
fmt->Rshift == 16 ? 1 : \
0 ); \
og = (fmt->Gshift == 0 ? 3 : \
fmt->Gshift == 8 ? 2 : \
fmt->Gshift == 16 ? 1 : \
0 ); \
ob = (fmt->Bshift == 0 ? 3 : \
fmt->Bshift == 8 ? 2 : \
fmt->Bshift == 16 ? 1 : \
0 ); \
}
#endif
#define CREATE_PIXEL(buf, r, g, b, a, bp, ft) \
switch (bp) \
{ \
case 2: \
*((Uint16 *) (buf)) = \
((r >> ft->Rloss) << ft->Rshift) | \
((g >> ft->Gloss) << ft->Gshift) | \
((b >> ft->Bloss) << ft->Bshift) | \
((a >> ft->Aloss) << ft->Ashift); \
break; \
case 4: \
*((Uint32 *) (buf)) = \
((r >> ft->Rloss) << ft->Rshift) | \
((g >> ft->Gloss) << ft->Gshift) | \
((b >> ft->Bloss) << ft->Bshift) | \
((a >> ft->Aloss) << ft->Ashift); \
break; \
}
/* Pretty good idea from Tom Duff :-). */
#define LOOP_UNROLLED4(code, n, width) \
n = (width + 3) / 4; \
switch (width & 3) \
{ \
case 0: do { code; \
case 3: code; \
case 2: code; \
case 1: code; \
} while (--n > 0); \
}
/* Used in the srcbpp == dstbpp == 1 blend functions */
#define REPEAT_3(code) \
code; \
code; \
code;
#define REPEAT_4(code) \
code; \
code; \
code; \
code;
#define BLEND_ADD(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \
tmp = dR + sR; dR = (tmp <= 255 ? tmp : 255); \
tmp = dG + sG; dG = (tmp <= 255 ? tmp : 255); \
tmp = dB + sB; dB = (tmp <= 255 ? tmp : 255);
#define BLEND_SUB(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \
tmp = dR - sR; dR = (tmp >= 0 ? tmp : 0); \
tmp = dG - sG; dG = (tmp >= 0 ? tmp : 0); \
tmp = dB - sB; dB = (tmp >= 0 ? tmp : 0);
#define BLEND_MULT(sR, sG, sB, sA, dR, dG, dB, dA) \
dR = (dR && sR) ? (dR * sR) >> 8 : 0; \
dG = (dG && sG) ? (dG * sG) >> 8 : 0; \
dB = (dB && sB) ? (dB * sB) >> 8 : 0;
#define BLEND_MIN(sR, sG, sB, sA, dR, dG, dB, dA) \
if(sR < dR) { dR = sR; } \
if(sG < dG) { dG = sG; } \
if(sB < dB) { dB = sB; }
#define BLEND_MAX(sR, sG, sB, sA, dR, dG, dB, dA) \
if(sR > dR) { dR = sR; } \
if(sG > dG) { dG = sG; } \
if(sB > dB) { dB = sB; }
#define BLEND_RGBA_ADD(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \
tmp = dR + sR; dR = (tmp <= 255 ? tmp : 255); \
tmp = dG + sG; dG = (tmp <= 255 ? tmp : 255); \
tmp = dB + sB; dB = (tmp <= 255 ? tmp : 255); \
tmp = dA + sA; dA = (tmp <= 255 ? tmp : 255);
#define BLEND_RGBA_SUB(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \
tmp = dR - sR; dR = (tmp >= 0 ? tmp : 0); \
tmp = dG - sG; dG = (tmp >= 0 ? tmp : 0); \
tmp = dB - sB; dB = (tmp >= 0 ? tmp : 0); \
tmp = dA - sA; dA = (tmp >= 0 ? tmp : 0);
#define BLEND_RGBA_MULT(sR, sG, sB, sA, dR, dG, dB, dA) \
dR = (dR && sR) ? (dR * sR) >> 8 : 0; \
dG = (dG && sG) ? (dG * sG) >> 8 : 0; \
dB = (dB && sB) ? (dB * sB) >> 8 : 0; \
dA = (dA && sA) ? (dA * sA) >> 8 : 0;
#define BLEND_RGBA_MIN(sR, sG, sB, sA, dR, dG, dB, dA) \
if(sR < dR) { dR = sR; } \
if(sG < dG) { dG = sG; } \
if(sB < dB) { dB = sB; } \
if(sA < dA) { dA = sA; }
#define BLEND_RGBA_MAX(sR, sG, sB, sA, dR, dG, dB, dA) \
if(sR > dR) { dR = sR; } \
if(sG > dG) { dG = sG; } \
if(sB > dB) { dB = sB; } \
if(sA > dA) { dA = sA; }
#if 1
/* Choose an alpha blend equation. If the sign is preserved on a right shift
* then use a specialized, faster, equation. Otherwise a more general form,
* where all additions are done before the shift, is needed.
*/
#if (-1 >> 1) < 0
#define ALPHA_BLEND_COMP(sC, dC, sA) ((((sC - dC) * sA + sC) >> 8) + dC)
#else
#define ALPHA_BLEND_COMP(sC, dC, sA) (((dC << 8) + (sC - dC) * sA + sC) >> 8)
#endif
#define ALPHA_BLEND(sR, sG, sB, sA, dR, dG, dB, dA) \
do { \
if (dA) \
{ \
dR = ALPHA_BLEND_COMP(sR, dR, sA); \
dG = ALPHA_BLEND_COMP(sG, dG, sA); \
dB = ALPHA_BLEND_COMP(sB, dB, sA); \
dA = sA + dA - ((sA * dA) / 255); \
} \
else \
{ \
dR = sR; \
dG = sG; \
dB = sB; \
dA = sA; \
} \
} while(0)
#define ALPHA_BLEND_PREMULTIPLIED_COMP(sC, dC, sA) (sC + dC - ((dC * sA) >> 8))
#define ALPHA_BLEND_PREMULTIPLIED(tmp, sR, sG, sB, sA, dR, dG, dB, dA) \
do { \
tmp = ALPHA_BLEND_PREMULTIPLIED_COMP(sR, dR, sA); dR = (tmp > 255 ? 255 : tmp); \
tmp = ALPHA_BLEND_PREMULTIPLIED_COMP(sG, dG, sA); dG = (tmp > 255 ? 255 : tmp); \
tmp = ALPHA_BLEND_PREMULTIPLIED_COMP(sB, dB, sA); dB = (tmp > 255 ? 255 : tmp); \
dA = sA + dA - ((sA * dA) / 255); \
} while(0)
#elif 0
#define ALPHA_BLEND(sR, sG, sB, sA, dR, dG, dB, dA) \
do { \
if(sA){ \
if(dA && sA < 255){ \
int dContrib = dA*(255 - sA)/255; \
dA = sA+dA - ((sA*dA)/255); \
dR = (dR*dContrib + sR*sA)/dA; \
dG = (dG*dContrib + sG*sA)/dA; \
dB = (dB*dContrib + sB*sA)/dA; \
}else{ \
dR = sR; \
dG = sG; \
dB = sB; \
dA = sA; \
} \
} \
} while(0)
#endif
int
surface_fill_blend (SDL_Surface *surface, SDL_Rect *rect, Uint32 color,
int blendargs);
void
surface_respect_clip_rect (SDL_Surface *surface, SDL_Rect *rect);
int
pygame_AlphaBlit (SDL_Surface * src, SDL_Rect * srcrect,
SDL_Surface * dst, SDL_Rect * dstrect, int the_args);
int
pygame_Blit (SDL_Surface * src, SDL_Rect * srcrect,
SDL_Surface * dst, SDL_Rect * dstrect, int the_args);
#endif /* SURFACE_H */

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,119 @@
#
# The Python Imaging Library
# $Id$
#
# bitmap distribution font (bdf) file parser
#
# history:
# 1996-05-16 fl created (as bdf2pil)
# 1997-08-25 fl converted to FontFile driver
# 2001-05-25 fl removed bogus __init__ call
# 2002-11-20 fl robustification (from Kevin Cazabon, Dmitry Vasiliev)
# 2003-04-22 fl more robustification (from Graham Dumpleton)
#
# Copyright (c) 1997-2003 by Secret Labs AB.
# Copyright (c) 1997-2003 by Fredrik Lundh.
#
# See the README file for information on usage and redistribution.
#
from __future__ import print_function
from . import Image, FontFile
# --------------------------------------------------------------------
# parse X Bitmap Distribution Format (BDF)
# --------------------------------------------------------------------
bdf_slant = {
"R": "Roman",
"I": "Italic",
"O": "Oblique",
"RI": "Reverse Italic",
"RO": "Reverse Oblique",
"OT": "Other"
}
bdf_spacing = {
"P": "Proportional",
"M": "Monospaced",
"C": "Cell"
}
def bdf_char(f):
# skip to STARTCHAR
while True:
s = f.readline()
if not s:
return None
if s[:9] == b"STARTCHAR":
break
id = s[9:].strip().decode('ascii')
# load symbol properties
props = {}
while True:
s = f.readline()
if not s or s[:6] == b"BITMAP":
break
i = s.find(b" ")
props[s[:i].decode('ascii')] = s[i+1:-1].decode('ascii')
# load bitmap
bitmap = []
while True:
s = f.readline()
if not s or s[:7] == b"ENDCHAR":
break
bitmap.append(s[:-1])
bitmap = b"".join(bitmap)
[x, y, l, d] = [int(p) for p in props["BBX"].split()]
[dx, dy] = [int(p) for p in props["DWIDTH"].split()]
bbox = (dx, dy), (l, -d-y, x+l, -d), (0, 0, x, y)
try:
im = Image.frombytes("1", (x, y), bitmap, "hex", "1")
except ValueError:
# deal with zero-width characters
im = Image.new("1", (x, y))
return id, int(props["ENCODING"]), bbox, im
##
# Font file plugin for the X11 BDF format.
class BdfFontFile(FontFile.FontFile):
def __init__(self, fp):
FontFile.FontFile.__init__(self)
s = fp.readline()
if s[:13] != b"STARTFONT 2.1":
raise SyntaxError("not a valid BDF file")
props = {}
comments = []
while True:
s = fp.readline()
if not s or s[:13] == b"ENDPROPERTIES":
break
i = s.find(b" ")
props[s[:i].decode('ascii')] = s[i+1:-1].decode('ascii')
if s[:i] in [b"COMMENT", b"COPYRIGHT"]:
if s.find(b"LogicalFontDescription") < 0:
comments.append(s[i+1:-1].decode('ascii'))
while True:
c = bdf_char(fp)
if not c:
break
id, ch, (xy, dst, src), im = c
if 0 <= ch < len(self.glyph):
self.glyph[ch] = xy, dst, src, im

View File

@ -0,0 +1,435 @@
"""
Blizzard Mipmap Format (.blp)
Jerome Leclanche <jerome@leclan.ch>
The contents of this file are hereby released in the public domain (CC0)
Full text of the CC0 license:
https://creativecommons.org/publicdomain/zero/1.0/
BLP1 files, used mostly in Warcraft III, are not fully supported.
All types of BLP2 files used in World of Warcraft are supported.
The BLP file structure consists of a header, up to 16 mipmaps of the
texture
Texture sizes must be powers of two, though the two dimensions do
not have to be equal; 512x256 is valid, but 512x200 is not.
The first mipmap (mipmap #0) is the full size image; each subsequent
mipmap halves both dimensions. The final mipmap should be 1x1.
BLP files come in many different flavours:
* JPEG-compressed (type == 0) - only supported for BLP1.
* RAW images (type == 1, encoding == 1). Each mipmap is stored as an
array of 8-bit values, one per pixel, left to right, top to bottom.
Each value is an index to the palette.
* DXT-compressed (type == 1, encoding == 2):
- DXT1 compression is used if alpha_encoding == 0.
- An additional alpha bit is used if alpha_depth == 1.
- DXT3 compression is used if alpha_encoding == 1.
- DXT5 compression is used if alpha_encoding == 7.
"""
import struct
from io import BytesIO
from . import Image, ImageFile
BLP_FORMAT_JPEG = 0
BLP_ENCODING_UNCOMPRESSED = 1
BLP_ENCODING_DXT = 2
BLP_ENCODING_UNCOMPRESSED_RAW_BGRA = 3
BLP_ALPHA_ENCODING_DXT1 = 0
BLP_ALPHA_ENCODING_DXT3 = 1
BLP_ALPHA_ENCODING_DXT5 = 7
def unpack_565(i):
return (
((i >> 11) & 0x1f) << 3,
((i >> 5) & 0x3f) << 2,
(i & 0x1f) << 3
)
def decode_dxt1(data, alpha=False):
"""
input: one "row" of data (i.e. will produce 4*width pixels)
"""
blocks = len(data) // 8 # number of blocks in row
ret = (bytearray(), bytearray(), bytearray(), bytearray())
for block in range(blocks):
# Decode next 8-byte block.
idx = block * 8
color0, color1, bits = struct.unpack_from("<HHI", data, idx)
r0, g0, b0 = unpack_565(color0)
r1, g1, b1 = unpack_565(color1)
# Decode this block into 4x4 pixels
# Accumulate the results onto our 4 row accumulators
for j in range(4):
for i in range(4):
# get next control op and generate a pixel
control = bits & 3
bits = bits >> 2
a = 0xFF
if control == 0:
r, g, b = r0, g0, b0
elif control == 1:
r, g, b = r1, g1, b1
elif control == 2:
if color0 > color1:
r = (2 * r0 + r1) // 3
g = (2 * g0 + g1) // 3
b = (2 * b0 + b1) // 3
else:
r = (r0 + r1) // 2
g = (g0 + g1) // 2
b = (b0 + b1) // 2
elif control == 3:
if color0 > color1:
r = (2 * r1 + r0) // 3
g = (2 * g1 + g0) // 3
b = (2 * b1 + b0) // 3
else:
r, g, b, a = 0, 0, 0, 0
if alpha:
ret[j].extend([r, g, b, a])
else:
ret[j].extend([r, g, b])
return ret
def decode_dxt3(data):
"""
input: one "row" of data (i.e. will produce 4*width pixels)
"""
blocks = len(data) // 16 # number of blocks in row
ret = (bytearray(), bytearray(), bytearray(), bytearray())
for block in range(blocks):
idx = block * 16
block = data[idx:idx + 16]
# Decode next 16-byte block.
bits = struct.unpack_from("<8B", block)
color0, color1 = struct.unpack_from("<HH", block, 8)
code, = struct.unpack_from("<I", block, 12)
r0, g0, b0 = unpack_565(color0)
r1, g1, b1 = unpack_565(color1)
for j in range(4):
high = False # Do we want the higher bits?
for i in range(4):
alphacode_index = (4 * j + i) // 2
a = bits[alphacode_index]
if high:
high = False
a >>= 4
else:
high = True
a &= 0xf
a *= 17 # We get a value between 0 and 15
color_code = (code >> 2 * (4 * j + i)) & 0x03
if color_code == 0:
r, g, b = r0, g0, b0
elif color_code == 1:
r, g, b = r1, g1, b1
elif color_code == 2:
r = (2 * r0 + r1) // 3
g = (2 * g0 + g1) // 3
b = (2 * b0 + b1) // 3
elif color_code == 3:
r = (2 * r1 + r0) // 3
g = (2 * g1 + g0) // 3
b = (2 * b1 + b0) // 3
ret[j].extend([r, g, b, a])
return ret
def decode_dxt5(data):
"""
input: one "row" of data (i.e. will produce 4 * width pixels)
"""
blocks = len(data) // 16 # number of blocks in row
ret = (bytearray(), bytearray(), bytearray(), bytearray())
for block in range(blocks):
idx = block * 16
block = data[idx:idx + 16]
# Decode next 16-byte block.
a0, a1 = struct.unpack_from("<BB", block)
bits = struct.unpack_from("<6B", block, 2)
alphacode1 = (
bits[2] | (bits[3] << 8) | (bits[4] << 16) | (bits[5] << 24)
)
alphacode2 = bits[0] | (bits[1] << 8)
color0, color1 = struct.unpack_from("<HH", block, 8)
code, = struct.unpack_from("<I", block, 12)
r0, g0, b0 = unpack_565(color0)
r1, g1, b1 = unpack_565(color1)
for j in range(4):
for i in range(4):
# get next control op and generate a pixel
alphacode_index = 3 * (4 * j + i)
if alphacode_index <= 12:
alphacode = (alphacode2 >> alphacode_index) & 0x07
elif alphacode_index == 15:
alphacode = (alphacode2 >> 15) | ((alphacode1 << 1) & 0x06)
else: # alphacode_index >= 18 and alphacode_index <= 45
alphacode = (alphacode1 >> (alphacode_index - 16)) & 0x07
if alphacode == 0:
a = a0
elif alphacode == 1:
a = a1
elif a0 > a1:
a = ((8 - alphacode) * a0 + (alphacode - 1) * a1) // 7
elif alphacode == 6:
a = 0
elif alphacode == 7:
a = 255
else:
a = ((6 - alphacode) * a0 + (alphacode - 1) * a1) // 5
color_code = (code >> 2 * (4 * j + i)) & 0x03
if color_code == 0:
r, g, b = r0, g0, b0
elif color_code == 1:
r, g, b = r1, g1, b1
elif color_code == 2:
r = (2 * r0 + r1) // 3
g = (2 * g0 + g1) // 3
b = (2 * b0 + b1) // 3
elif color_code == 3:
r = (2 * r1 + r0) // 3
g = (2 * g1 + g0) // 3
b = (2 * b1 + b0) // 3
ret[j].extend([r, g, b, a])
return ret
class BLPFormatError(NotImplementedError):
pass
class BlpImageFile(ImageFile.ImageFile):
"""
Blizzard Mipmap Format
"""
format = "BLP"
format_description = "Blizzard Mipmap Format"
def _open(self):
self.magic = self.fp.read(4)
self._read_blp_header()
if self.magic == b"BLP1":
decoder = "BLP1"
self.mode = "RGB"
elif self.magic == b"BLP2":
decoder = "BLP2"
self.mode = "RGBA" if self._blp_alpha_depth else "RGB"
else:
raise BLPFormatError("Bad BLP magic %r" % (self.magic))
self.tile = [
(decoder, (0, 0) + self.size, 0, (self.mode, 0, 1))
]
def _read_blp_header(self):
self._blp_compression, = struct.unpack("<i", self.fp.read(4))
self._blp_encoding, = struct.unpack("<b", self.fp.read(1))
self._blp_alpha_depth, = struct.unpack("<b", self.fp.read(1))
self._blp_alpha_encoding, = struct.unpack("<b", self.fp.read(1))
self._blp_mips, = struct.unpack("<b", self.fp.read(1))
self._size = struct.unpack("<II", self.fp.read(8))
if self.magic == b"BLP1":
# Only present for BLP1
self._blp_encoding, = struct.unpack("<i", self.fp.read(4))
self._blp_subtype, = struct.unpack("<i", self.fp.read(4))
self._blp_offsets = struct.unpack("<16I", self.fp.read(16 * 4))
self._blp_lengths = struct.unpack("<16I", self.fp.read(16 * 4))
class _BLPBaseDecoder(ImageFile.PyDecoder):
_pulls_fd = True
def decode(self, buffer):
try:
self.fd.seek(0)
self.magic = self.fd.read(4)
self._read_blp_header()
self._load()
except struct.error:
raise IOError("Truncated Blp file")
return 0, 0
def _read_palette(self):
ret = []
for i in range(256):
try:
b, g, r, a = struct.unpack("<4B", self.fd.read(4))
except struct.error:
break
ret.append((b, g, r, a))
return ret
def _read_blp_header(self):
self._blp_compression, = struct.unpack("<i", self.fd.read(4))
self._blp_encoding, = struct.unpack("<b", self.fd.read(1))
self._blp_alpha_depth, = struct.unpack("<b", self.fd.read(1))
self._blp_alpha_encoding, = struct.unpack("<b", self.fd.read(1))
self._blp_mips, = struct.unpack("<b", self.fd.read(1))
self.size = struct.unpack("<II", self.fd.read(8))
if self.magic == b"BLP1":
# Only present for BLP1
self._blp_encoding, = struct.unpack("<i", self.fd.read(4))
self._blp_subtype, = struct.unpack("<i", self.fd.read(4))
self._blp_offsets = struct.unpack("<16I", self.fd.read(16 * 4))
self._blp_lengths = struct.unpack("<16I", self.fd.read(16 * 4))
class BLP1Decoder(_BLPBaseDecoder):
def _load(self):
if self._blp_compression == BLP_FORMAT_JPEG:
self._decode_jpeg_stream()
elif self._blp_compression == 1:
if self._blp_encoding in (4, 5):
data = bytearray()
palette = self._read_palette()
_data = BytesIO(self.fd.read(self._blp_lengths[0]))
while True:
try:
offset, = struct.unpack("<B", _data.read(1))
except struct.error:
break
b, g, r, a = palette[offset]
data.extend([r, g, b])
self.set_as_raw(bytes(data))
else:
raise BLPFormatError(
"Unsupported BLP encoding %r" % (self._blp_encoding)
)
else:
raise BLPFormatError(
"Unsupported BLP compression %r" % (self._blp_encoding)
)
def _decode_jpeg_stream(self):
from PIL.JpegImagePlugin import JpegImageFile
jpeg_header_size, = struct.unpack("<I", self.fd.read(4))
jpeg_header = self.fd.read(jpeg_header_size)
self.fd.read(self._blp_offsets[0] - self.fd.tell()) # What IS this?
data = self.fd.read(self._blp_lengths[0])
data = jpeg_header + data
data = BytesIO(data)
image = JpegImageFile(data)
self.tile = image.tile # :/
self.fd = image.fp
self.mode = image.mode
class BLP2Decoder(_BLPBaseDecoder):
def _load(self):
palette = self._read_palette()
data = bytearray()
self.fd.seek(self._blp_offsets[0])
if self._blp_compression == 1:
# Uncompressed or DirectX compression
if self._blp_encoding == BLP_ENCODING_UNCOMPRESSED:
_data = BytesIO(self.fd.read(self._blp_lengths[0]))
while True:
try:
offset, = struct.unpack("<B", _data.read(1))
except struct.error:
break
b, g, r, a = palette[offset]
data.extend((r, g, b))
elif self._blp_encoding == BLP_ENCODING_DXT:
if self._blp_alpha_encoding == BLP_ALPHA_ENCODING_DXT1:
linesize = (self.size[0] + 3) // 4 * 8
for yb in range((self.size[1] + 3) // 4):
for d in decode_dxt1(
self.fd.read(linesize),
alpha=bool(self._blp_alpha_depth)
):
data += d
elif self._blp_alpha_encoding == BLP_ALPHA_ENCODING_DXT3:
linesize = (self.size[0] + 3) // 4 * 16
for yb in range((self.size[1] + 3) // 4):
for d in decode_dxt3(self.fd.read(linesize)):
data += d
elif self._blp_alpha_encoding == BLP_ALPHA_ENCODING_DXT5:
linesize = (self.size[0] + 3) // 4 * 16
for yb in range((self.size[1] + 3) // 4):
for d in decode_dxt5(self.fd.read(linesize)):
data += d
else:
raise BLPFormatError("Unsupported alpha encoding %r" % (
self._blp_alpha_encoding
))
else:
raise BLPFormatError(
"Unknown BLP encoding %r" % (self._blp_encoding)
)
else:
raise BLPFormatError(
"Unknown BLP compression %r" % (self._blp_compression)
)
self.set_as_raw(bytes(data))
Image.register_open(
BlpImageFile.format, BlpImageFile, lambda p: p[:4] in (b"BLP1", b"BLP2")
)
Image.register_extension(BlpImageFile.format, ".blp")
Image.register_decoder("BLP1", BLP1Decoder)
Image.register_decoder("BLP2", BLP2Decoder)

View File

@ -0,0 +1,352 @@
#
# The Python Imaging Library.
# $Id$
#
# BMP file handler
#
# Windows (and OS/2) native bitmap storage format.
#
# history:
# 1995-09-01 fl Created
# 1996-04-30 fl Added save
# 1997-08-27 fl Fixed save of 1-bit images
# 1998-03-06 fl Load P images as L where possible
# 1998-07-03 fl Load P images as 1 where possible
# 1998-12-29 fl Handle small palettes
# 2002-12-30 fl Fixed load of 1-bit palette images
# 2003-04-21 fl Fixed load of 1-bit monochrome images
# 2003-04-23 fl Added limited support for BI_BITFIELDS compression
#
# Copyright (c) 1997-2003 by Secret Labs AB
# Copyright (c) 1995-2003 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
from . import Image, ImageFile, ImagePalette
from ._binary import i8, i16le as i16, i32le as i32, \
o8, o16le as o16, o32le as o32
import math
__version__ = "0.7"
#
# --------------------------------------------------------------------
# Read BMP file
BIT2MODE = {
# bits => mode, rawmode
1: ("P", "P;1"),
4: ("P", "P;4"),
8: ("P", "P"),
16: ("RGB", "BGR;15"),
24: ("RGB", "BGR"),
32: ("RGB", "BGRX"),
}
def _accept(prefix):
return prefix[:2] == b"BM"
# =============================================================================
# Image plugin for the Windows BMP format.
# =============================================================================
class BmpImageFile(ImageFile.ImageFile):
""" Image plugin for the Windows Bitmap format (BMP) """
# ------------------------------------------------------------- Description
format_description = "Windows Bitmap"
format = "BMP"
# -------------------------------------------------- BMP Compression values
COMPRESSIONS = {
'RAW': 0,
'RLE8': 1,
'RLE4': 2,
'BITFIELDS': 3,
'JPEG': 4,
'PNG': 5
}
RAW, RLE8, RLE4, BITFIELDS, JPEG, PNG = 0, 1, 2, 3, 4, 5
def _bitmap(self, header=0, offset=0):
""" Read relevant info about the BMP """
read, seek = self.fp.read, self.fp.seek
if header:
seek(header)
file_info = {}
# read bmp header size @offset 14 (this is part of the header size)
file_info['header_size'] = i32(read(4))
file_info['direction'] = -1
# -------------------- If requested, read header at a specific position
# read the rest of the bmp header, without its size
header_data = ImageFile._safe_read(self.fp,
file_info['header_size'] - 4)
# -------------------------------------------------- IBM OS/2 Bitmap v1
# ----- This format has different offsets because of width/height types
if file_info['header_size'] == 12:
file_info['width'] = i16(header_data[0:2])
file_info['height'] = i16(header_data[2:4])
file_info['planes'] = i16(header_data[4:6])
file_info['bits'] = i16(header_data[6:8])
file_info['compression'] = self.RAW
file_info['palette_padding'] = 3
# --------------------------------------------- Windows Bitmap v2 to v5
# v3, OS/2 v2, v4, v5
elif file_info['header_size'] in (40, 64, 108, 124):
if file_info['header_size'] >= 40: # v3 and OS/2
file_info['y_flip'] = i8(header_data[7]) == 0xff
file_info['direction'] = 1 if file_info['y_flip'] else -1
file_info['width'] = i32(header_data[0:4])
file_info['height'] = (i32(header_data[4:8])
if not file_info['y_flip']
else 2**32 - i32(header_data[4:8]))
file_info['planes'] = i16(header_data[8:10])
file_info['bits'] = i16(header_data[10:12])
file_info['compression'] = i32(header_data[12:16])
# byte size of pixel data
file_info['data_size'] = i32(header_data[16:20])
file_info['pixels_per_meter'] = (i32(header_data[20:24]),
i32(header_data[24:28]))
file_info['colors'] = i32(header_data[28:32])
file_info['palette_padding'] = 4
self.info["dpi"] = tuple(
map(lambda x: int(math.ceil(x / 39.3701)),
file_info['pixels_per_meter']))
if file_info['compression'] == self.BITFIELDS:
if len(header_data) >= 52:
for idx, mask in enumerate(['r_mask',
'g_mask',
'b_mask',
'a_mask']):
file_info[mask] = i32(
header_data[36 + idx * 4:40 + idx * 4]
)
else:
# 40 byte headers only have the three components in the
# bitfields masks, ref:
# https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
# See also
# https://github.com/python-pillow/Pillow/issues/1293
# There is a 4th component in the RGBQuad, in the alpha
# location, but it is listed as a reserved component,
# and it is not generally an alpha channel
file_info['a_mask'] = 0x0
for mask in ['r_mask', 'g_mask', 'b_mask']:
file_info[mask] = i32(read(4))
file_info['rgb_mask'] = (file_info['r_mask'],
file_info['g_mask'],
file_info['b_mask'])
file_info['rgba_mask'] = (file_info['r_mask'],
file_info['g_mask'],
file_info['b_mask'],
file_info['a_mask'])
else:
raise IOError("Unsupported BMP header type (%d)" %
file_info['header_size'])
# ------------------ Special case : header is reported 40, which
# ---------------------- is shorter than real size for bpp >= 16
self._size = file_info['width'], file_info['height']
# ------- If color count was not found in the header, compute from bits
file_info["colors"] = (file_info["colors"]
if file_info.get("colors", 0)
else (1 << file_info["bits"]))
# ------------------------------- Check abnormal values for DOS attacks
if file_info['width'] * file_info['height'] > 2**31:
raise IOError("Unsupported BMP Size: (%dx%d)" % self.size)
# ---------------------- Check bit depth for unusual unsupported values
self.mode, raw_mode = BIT2MODE.get(file_info['bits'], (None, None))
if self.mode is None:
raise IOError("Unsupported BMP pixel depth (%d)"
% file_info['bits'])
# ---------------- Process BMP with Bitfields compression (not palette)
if file_info['compression'] == self.BITFIELDS:
SUPPORTED = {
32: [(0xff0000, 0xff00, 0xff, 0x0),
(0xff0000, 0xff00, 0xff, 0xff000000),
(0x0, 0x0, 0x0, 0x0),
(0xff000000, 0xff0000, 0xff00, 0x0)],
24: [(0xff0000, 0xff00, 0xff)],
16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]
}
MASK_MODES = {
(32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX",
(32, (0xff000000, 0xff0000, 0xff00, 0x0)): "XBGR",
(32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA",
(32, (0x0, 0x0, 0x0, 0x0)): "BGRA",
(24, (0xff0000, 0xff00, 0xff)): "BGR",
(16, (0xf800, 0x7e0, 0x1f)): "BGR;16",
(16, (0x7c00, 0x3e0, 0x1f)): "BGR;15"
}
if file_info['bits'] in SUPPORTED:
if file_info['bits'] == 32 and \
file_info['rgba_mask'] in SUPPORTED[file_info['bits']]:
raw_mode = MASK_MODES[
(file_info["bits"], file_info["rgba_mask"])
]
self.mode = "RGBA" if raw_mode in ("BGRA",) else self.mode
elif (file_info['bits'] in (24, 16) and
file_info['rgb_mask'] in SUPPORTED[file_info['bits']]):
raw_mode = MASK_MODES[
(file_info['bits'], file_info['rgb_mask'])
]
else:
raise IOError("Unsupported BMP bitfields layout")
else:
raise IOError("Unsupported BMP bitfields layout")
elif file_info['compression'] == self.RAW:
if file_info['bits'] == 32 and header == 22: # 32-bit .cur offset
raw_mode, self.mode = "BGRA", "RGBA"
else:
raise IOError("Unsupported BMP compression (%d)" %
file_info['compression'])
# --------------- Once the header is processed, process the palette/LUT
if self.mode == "P": # Paletted for 1, 4 and 8 bit images
# ---------------------------------------------------- 1-bit images
if not (0 < file_info['colors'] <= 65536):
raise IOError("Unsupported BMP Palette size (%d)" %
file_info['colors'])
else:
padding = file_info['palette_padding']
palette = read(padding * file_info['colors'])
greyscale = True
indices = (0, 255) if file_info['colors'] == 2 else \
list(range(file_info['colors']))
# ----------------- Check if greyscale and ignore palette if so
for ind, val in enumerate(indices):
rgb = palette[ind*padding:ind*padding + 3]
if rgb != o8(val) * 3:
greyscale = False
# ------- If all colors are grey, white or black, ditch palette
if greyscale:
self.mode = "1" if file_info['colors'] == 2 else "L"
raw_mode = self.mode
else:
self.mode = "P"
self.palette = ImagePalette.raw(
"BGRX" if padding == 4 else "BGR", palette)
# ---------------------------- Finally set the tile data for the plugin
self.info['compression'] = file_info['compression']
self.tile = [
('raw',
(0, 0, file_info['width'], file_info['height']),
offset or self.fp.tell(),
(raw_mode,
((file_info['width'] * file_info['bits'] + 31) >> 3) & (~3),
file_info['direction']))
]
def _open(self):
""" Open file, check magic number and read header """
# read 14 bytes: magic number, filesize, reserved, header final offset
head_data = self.fp.read(14)
# choke if the file does not have the required magic bytes
if head_data[0:2] != b"BM":
raise SyntaxError("Not a BMP file")
# read the start position of the BMP image data (u32)
offset = i32(head_data[10:14])
# load bitmap information (offset=raster info)
self._bitmap(offset=offset)
# =============================================================================
# Image plugin for the DIB format (BMP alias)
# =============================================================================
class DibImageFile(BmpImageFile):
format = "DIB"
format_description = "Windows Bitmap"
def _open(self):
self._bitmap()
#
# --------------------------------------------------------------------
# Write BMP file
SAVE = {
"1": ("1", 1, 2),
"L": ("L", 8, 256),
"P": ("P", 8, 256),
"RGB": ("BGR", 24, 0),
"RGBA": ("BGRA", 32, 0),
}
def _save(im, fp, filename):
try:
rawmode, bits, colors = SAVE[im.mode]
except KeyError:
raise IOError("cannot write mode %s as BMP" % im.mode)
info = im.encoderinfo
dpi = info.get("dpi", (96, 96))
# 1 meter == 39.3701 inches
ppm = tuple(map(lambda x: int(x * 39.3701), dpi))
stride = ((im.size[0]*bits+7)//8+3) & (~3)
header = 40 # or 64 for OS/2 version 2
offset = 14 + header + colors * 4
image = stride * im.size[1]
# bitmap header
fp.write(b"BM" + # file type (magic)
o32(offset+image) + # file size
o32(0) + # reserved
o32(offset)) # image data offset
# bitmap info header
fp.write(o32(header) + # info header size
o32(im.size[0]) + # width
o32(im.size[1]) + # height
o16(1) + # planes
o16(bits) + # depth
o32(0) + # compression (0=uncompressed)
o32(image) + # size of bitmap
o32(ppm[0]) + o32(ppm[1]) + # resolution
o32(colors) + # colors used
o32(colors)) # colors important
fp.write(b"\0" * (header - 40)) # padding (for OS/2 format)
if im.mode == "1":
for i in (0, 255):
fp.write(o8(i) * 4)
elif im.mode == "L":
for i in range(256):
fp.write(o8(i) * 4)
elif im.mode == "P":
fp.write(im.im.getpalette("RGB", "BGRX"))
ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0,
(rawmode, stride, -1))])
#
# --------------------------------------------------------------------
# Registry
Image.register_open(BmpImageFile.format, BmpImageFile, _accept)
Image.register_save(BmpImageFile.format, _save)
Image.register_extension(BmpImageFile.format, ".bmp")
Image.register_mime(BmpImageFile.format, "image/bmp")

View File

@ -0,0 +1,72 @@
#
# The Python Imaging Library
# $Id$
#
# BUFR stub adapter
#
# Copyright (c) 1996-2003 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
from . import Image, ImageFile
_handler = None
def register_handler(handler):
"""
Install application-specific BUFR image handler.
:param handler: Handler object.
"""
global _handler
_handler = handler
# --------------------------------------------------------------------
# Image adapter
def _accept(prefix):
return prefix[:4] == b"BUFR" or prefix[:4] == b"ZCZC"
class BufrStubImageFile(ImageFile.StubImageFile):
format = "BUFR"
format_description = "BUFR"
def _open(self):
offset = self.fp.tell()
if not _accept(self.fp.read(4)):
raise SyntaxError("Not a BUFR file")
self.fp.seek(offset)
# make something up
self.mode = "F"
self._size = 1, 1
loader = self._load()
if loader:
loader.open(self)
def _load(self):
return _handler
def _save(im, fp, filename):
if _handler is None or not hasattr("_handler", "save"):
raise IOError("BUFR save handler not installed")
_handler.save(im, fp, filename)
# --------------------------------------------------------------------
# Registry
Image.register_open(BufrStubImageFile.format, BufrStubImageFile, _accept)
Image.register_save(BufrStubImageFile.format, _save)
Image.register_extension(BufrStubImageFile.format, ".bufr")

View File

@ -0,0 +1,116 @@
#
# The Python Imaging Library.
# $Id$
#
# a class to read from a container file
#
# History:
# 1995-06-18 fl Created
# 1995-09-07 fl Added readline(), readlines()
#
# Copyright (c) 1997-2001 by Secret Labs AB
# Copyright (c) 1995 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
##
# A file object that provides read access to a part of an existing
# file (for example a TAR file).
class ContainerIO(object):
def __init__(self, file, offset, length):
"""
Create file object.
:param file: Existing file.
:param offset: Start of region, in bytes.
:param length: Size of region, in bytes.
"""
self.fh = file
self.pos = 0
self.offset = offset
self.length = length
self.fh.seek(offset)
##
# Always false.
def isatty(self):
return 0
def seek(self, offset, mode=0):
"""
Move file pointer.
:param offset: Offset in bytes.
:param mode: Starting position. Use 0 for beginning of region, 1
for current offset, and 2 for end of region. You cannot move
the pointer outside the defined region.
"""
if mode == 1:
self.pos = self.pos + offset
elif mode == 2:
self.pos = self.length + offset
else:
self.pos = offset
# clamp
self.pos = max(0, min(self.pos, self.length))
self.fh.seek(self.offset + self.pos)
def tell(self):
"""
Get current file pointer.
:returns: Offset from start of region, in bytes.
"""
return self.pos
def read(self, n=0):
"""
Read data.
:param n: Number of bytes to read. If omitted or zero,
read until end of region.
:returns: An 8-bit string.
"""
if n:
n = min(n, self.length - self.pos)
else:
n = self.length - self.pos
if not n: # EOF
return ""
self.pos = self.pos + n
return self.fh.read(n)
def readline(self):
"""
Read a line of text.
:returns: An 8-bit string.
"""
s = ""
while True:
c = self.read(1)
if not c:
break
s = s + c
if c == "\n":
break
return s
def readlines(self):
"""
Read multiple lines of text.
:returns: A list of 8-bit strings.
"""
lines = []
while True:
s = self.readline()
if not s:
break
lines.append(s)
return lines

View File

@ -0,0 +1,78 @@
#
# The Python Imaging Library.
# $Id$
#
# Windows Cursor support for PIL
#
# notes:
# uses BmpImagePlugin.py to read the bitmap data.
#
# history:
# 96-05-27 fl Created
#
# Copyright (c) Secret Labs AB 1997.
# Copyright (c) Fredrik Lundh 1996.
#
# See the README file for information on usage and redistribution.
#
from __future__ import print_function
from . import Image, BmpImagePlugin
from ._binary import i8, i16le as i16, i32le as i32
__version__ = "0.1"
#
# --------------------------------------------------------------------
def _accept(prefix):
return prefix[:4] == b"\0\0\2\0"
##
# Image plugin for Windows Cursor files.
class CurImageFile(BmpImagePlugin.BmpImageFile):
format = "CUR"
format_description = "Windows Cursor"
def _open(self):
offset = self.fp.tell()
# check magic
s = self.fp.read(6)
if not _accept(s):
raise SyntaxError("not a CUR file")
# pick the largest cursor in the file
m = b""
for i in range(i16(s[4:])):
s = self.fp.read(16)
if not m:
m = s
elif i8(s[0]) > i8(m[0]) and i8(s[1]) > i8(m[1]):
m = s
if not m:
raise TypeError("No cursors were found")
# load as bitmap
self._bitmap(i32(m[12:]) + offset)
# patch up the bitmap height
self._size = self.size[0], self.size[1]//2
d, e, o, a = self.tile[0]
self.tile[0] = d, (0, 0)+self.size, o, a
return
#
# --------------------------------------------------------------------
Image.register_open(CurImageFile.format, CurImageFile, _accept)
Image.register_extension(CurImageFile.format, ".cur")

View File

@ -0,0 +1,96 @@
#
# The Python Imaging Library.
# $Id$
#
# DCX file handling
#
# DCX is a container file format defined by Intel, commonly used
# for fax applications. Each DCX file consists of a directory
# (a list of file offsets) followed by a set of (usually 1-bit)
# PCX files.
#
# History:
# 1995-09-09 fl Created
# 1996-03-20 fl Properly derived from PcxImageFile.
# 1998-07-15 fl Renamed offset attribute to avoid name clash
# 2002-07-30 fl Fixed file handling
#
# Copyright (c) 1997-98 by Secret Labs AB.
# Copyright (c) 1995-96 by Fredrik Lundh.
#
# See the README file for information on usage and redistribution.
#
from . import Image
from ._binary import i32le as i32
from .PcxImagePlugin import PcxImageFile
__version__ = "0.2"
MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then?
def _accept(prefix):
return len(prefix) >= 4 and i32(prefix) == MAGIC
##
# Image plugin for the Intel DCX format.
class DcxImageFile(PcxImageFile):
format = "DCX"
format_description = "Intel DCX"
_close_exclusive_fp_after_loading = False
def _open(self):
# Header
s = self.fp.read(4)
if i32(s) != MAGIC:
raise SyntaxError("not a DCX file")
# Component directory
self._offset = []
for i in range(1024):
offset = i32(self.fp.read(4))
if not offset:
break
self._offset.append(offset)
self.__fp = self.fp
self.frame = None
self.seek(0)
@property
def n_frames(self):
return len(self._offset)
@property
def is_animated(self):
return len(self._offset) > 1
def seek(self, frame):
if not self._seek_check(frame):
return
self.frame = frame
self.fp = self.__fp
self.fp.seek(self._offset[frame])
PcxImageFile._open(self)
def tell(self):
return self.frame
def _close__fp(self):
try:
if self.__fp != self.fp:
self.__fp.close()
except AttributeError:
pass
finally:
self.__fp = None
Image.register_open(DcxImageFile.format, DcxImageFile, _accept)
Image.register_extension(DcxImageFile.format, ".dcx")

View File

@ -0,0 +1,173 @@
"""
A Pillow loader for .dds files (S3TC-compressed aka DXTC)
Jerome Leclanche <jerome@leclan.ch>
Documentation:
https://web.archive.org/web/20170802060935/http://oss.sgi.com/projects/ogl-sample/registry/EXT/texture_compression_s3tc.txt
The contents of this file are hereby released in the public domain (CC0)
Full text of the CC0 license:
https://creativecommons.org/publicdomain/zero/1.0/
"""
import struct
from io import BytesIO
from . import Image, ImageFile
# Magic ("DDS ")
DDS_MAGIC = 0x20534444
# DDS flags
DDSD_CAPS = 0x1
DDSD_HEIGHT = 0x2
DDSD_WIDTH = 0x4
DDSD_PITCH = 0x8
DDSD_PIXELFORMAT = 0x1000
DDSD_MIPMAPCOUNT = 0x20000
DDSD_LINEARSIZE = 0x80000
DDSD_DEPTH = 0x800000
# DDS caps
DDSCAPS_COMPLEX = 0x8
DDSCAPS_TEXTURE = 0x1000
DDSCAPS_MIPMAP = 0x400000
DDSCAPS2_CUBEMAP = 0x200
DDSCAPS2_CUBEMAP_POSITIVEX = 0x400
DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800
DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000
DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000
DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000
DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000
DDSCAPS2_VOLUME = 0x200000
# Pixel Format
DDPF_ALPHAPIXELS = 0x1
DDPF_ALPHA = 0x2
DDPF_FOURCC = 0x4
DDPF_PALETTEINDEXED8 = 0x20
DDPF_RGB = 0x40
DDPF_LUMINANCE = 0x20000
# dds.h
DDS_FOURCC = DDPF_FOURCC
DDS_RGB = DDPF_RGB
DDS_RGBA = DDPF_RGB | DDPF_ALPHAPIXELS
DDS_LUMINANCE = DDPF_LUMINANCE
DDS_LUMINANCEA = DDPF_LUMINANCE | DDPF_ALPHAPIXELS
DDS_ALPHA = DDPF_ALPHA
DDS_PAL8 = DDPF_PALETTEINDEXED8
DDS_HEADER_FLAGS_TEXTURE = (DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH |
DDSD_PIXELFORMAT)
DDS_HEADER_FLAGS_MIPMAP = DDSD_MIPMAPCOUNT
DDS_HEADER_FLAGS_VOLUME = DDSD_DEPTH
DDS_HEADER_FLAGS_PITCH = DDSD_PITCH
DDS_HEADER_FLAGS_LINEARSIZE = DDSD_LINEARSIZE
DDS_HEIGHT = DDSD_HEIGHT
DDS_WIDTH = DDSD_WIDTH
DDS_SURFACE_FLAGS_TEXTURE = DDSCAPS_TEXTURE
DDS_SURFACE_FLAGS_MIPMAP = DDSCAPS_COMPLEX | DDSCAPS_MIPMAP
DDS_SURFACE_FLAGS_CUBEMAP = DDSCAPS_COMPLEX
DDS_CUBEMAP_POSITIVEX = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX
DDS_CUBEMAP_NEGATIVEX = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX
DDS_CUBEMAP_POSITIVEY = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY
DDS_CUBEMAP_NEGATIVEY = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY
DDS_CUBEMAP_POSITIVEZ = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ
DDS_CUBEMAP_NEGATIVEZ = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ
# DXT1
DXT1_FOURCC = 0x31545844
# DXT3
DXT3_FOURCC = 0x33545844
# DXT5
DXT5_FOURCC = 0x35545844
# dxgiformat.h
DXGI_FORMAT_BC7_TYPELESS = 97
DXGI_FORMAT_BC7_UNORM = 98
DXGI_FORMAT_BC7_UNORM_SRGB = 99
class DdsImageFile(ImageFile.ImageFile):
format = "DDS"
format_description = "DirectDraw Surface"
def _open(self):
magic, header_size = struct.unpack("<II", self.fp.read(8))
if header_size != 124:
raise IOError("Unsupported header size %r" % (header_size))
header_bytes = self.fp.read(header_size - 4)
if len(header_bytes) != 120:
raise IOError("Incomplete header: %s bytes" % len(header_bytes))
header = BytesIO(header_bytes)
flags, height, width = struct.unpack("<3I", header.read(12))
self._size = (width, height)
self.mode = "RGBA"
pitch, depth, mipmaps = struct.unpack("<3I", header.read(12))
struct.unpack("<11I", header.read(44)) # reserved
# pixel format
pfsize, pfflags = struct.unpack("<2I", header.read(8))
fourcc = header.read(4)
bitcount, rmask, gmask, bmask, amask = struct.unpack("<5I",
header.read(20))
data_start = header_size + 4
n = 0
if fourcc == b"DXT1":
self.pixel_format = "DXT1"
n = 1
elif fourcc == b"DXT3":
self.pixel_format = "DXT3"
n = 2
elif fourcc == b"DXT5":
self.pixel_format = "DXT5"
n = 3
elif fourcc == b"DX10":
data_start += 20
# ignoring flags which pertain to volume textures and cubemaps
dxt10 = BytesIO(self.fp.read(20))
dxgi_format, dimension = struct.unpack("<II", dxt10.read(8))
if dxgi_format in (DXGI_FORMAT_BC7_TYPELESS,
DXGI_FORMAT_BC7_UNORM):
self.pixel_format = "BC7"
n = 7
elif dxgi_format == DXGI_FORMAT_BC7_UNORM_SRGB:
self.pixel_format = "BC7"
self.im_info["gamma"] = 1/2.2
n = 7
else:
raise NotImplementedError("Unimplemented DXGI format %d" %
(dxgi_format))
else:
raise NotImplementedError("Unimplemented pixel format %r" %
(fourcc))
self.tile = [
("bcn", (0, 0) + self.size, data_start, (n))
]
def load_seek(self, pos):
pass
def _validate(prefix):
return prefix[:4] == b"DDS "
Image.register_open(DdsImageFile.format, DdsImageFile, _validate)
Image.register_extension(DdsImageFile.format, ".dds")

View File

@ -0,0 +1,419 @@
#
# The Python Imaging Library.
# $Id$
#
# EPS file handling
#
# History:
# 1995-09-01 fl Created (0.1)
# 1996-05-18 fl Don't choke on "atend" fields, Ghostscript interface (0.2)
# 1996-08-22 fl Don't choke on floating point BoundingBox values
# 1996-08-23 fl Handle files from Macintosh (0.3)
# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.4)
# 2003-09-07 fl Check gs.close status (from Federico Di Gregorio) (0.5)
# 2014-05-07 e Handling of EPS with binary preview and fixed resolution
# resizing
#
# Copyright (c) 1997-2003 by Secret Labs AB.
# Copyright (c) 1995-2003 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
import re
import io
import os
import sys
from . import Image, ImageFile
from ._binary import i32le as i32
__version__ = "0.5"
#
# --------------------------------------------------------------------
split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$")
field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$")
gs_windows_binary = None
if sys.platform.startswith('win'):
import shutil
if hasattr(shutil, 'which'):
which = shutil.which
else:
# Python 2
import distutils.spawn
which = distutils.spawn.find_executable
for binary in ('gswin32c', 'gswin64c', 'gs'):
if which(binary) is not None:
gs_windows_binary = binary
break
else:
gs_windows_binary = False
def has_ghostscript():
if gs_windows_binary:
return True
if not sys.platform.startswith('win'):
import subprocess
try:
with open(os.devnull, 'wb') as devnull:
subprocess.check_call(['gs', '--version'], stdout=devnull)
return True
except OSError:
# No Ghostscript
pass
return False
def Ghostscript(tile, size, fp, scale=1):
"""Render an image using Ghostscript"""
# Unpack decoder tile
decoder, tile, offset, data = tile[0]
length, bbox = data
# Hack to support hi-res rendering
scale = int(scale) or 1
# orig_size = size
# orig_bbox = bbox
size = (size[0] * scale, size[1] * scale)
# resolution is dependent on bbox and size
res = (float((72.0 * size[0]) / (bbox[2]-bbox[0])),
float((72.0 * size[1]) / (bbox[3]-bbox[1])))
import subprocess
import tempfile
out_fd, outfile = tempfile.mkstemp()
os.close(out_fd)
infile_temp = None
if hasattr(fp, 'name') and os.path.exists(fp.name):
infile = fp.name
else:
in_fd, infile_temp = tempfile.mkstemp()
os.close(in_fd)
infile = infile_temp
# Ignore length and offset!
# Ghostscript can read it
# Copy whole file to read in Ghostscript
with open(infile_temp, 'wb') as f:
# fetch length of fp
fp.seek(0, 2)
fsize = fp.tell()
# ensure start position
# go back
fp.seek(0)
lengthfile = fsize
while lengthfile > 0:
s = fp.read(min(lengthfile, 100*1024))
if not s:
break
lengthfile -= len(s)
f.write(s)
# Build Ghostscript command
command = ["gs",
"-q", # quiet mode
"-g%dx%d" % size, # set output geometry (pixels)
"-r%fx%f" % res, # set input DPI (dots per inch)
"-dBATCH", # exit after processing
"-dNOPAUSE", # don't pause between pages
"-dSAFER", # safe mode
"-sDEVICE=ppmraw", # ppm driver
"-sOutputFile=%s" % outfile, # output file
# adjust for image origin
"-c", "%d %d translate" % (-bbox[0], -bbox[1]),
"-f", infile, # input file
# showpage (see https://bugs.ghostscript.com/show_bug.cgi?id=698272)
"-c", "showpage",
]
if gs_windows_binary is not None:
if not gs_windows_binary:
raise WindowsError('Unable to locate Ghostscript on paths')
command[0] = gs_windows_binary
# push data through Ghostscript
try:
with open(os.devnull, 'w+b') as devnull:
startupinfo = None
if sys.platform.startswith('win'):
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
subprocess.check_call(command, stdin=devnull, stdout=devnull,
startupinfo=startupinfo)
im = Image.open(outfile)
im.load()
finally:
try:
os.unlink(outfile)
if infile_temp:
os.unlink(infile_temp)
except OSError:
pass
return im.im.copy()
class PSFile(object):
"""
Wrapper for bytesio object that treats either CR or LF as end of line.
"""
def __init__(self, fp):
self.fp = fp
self.char = None
def seek(self, offset, whence=0):
self.char = None
self.fp.seek(offset, whence)
def readline(self):
s = self.char or b""
self.char = None
c = self.fp.read(1)
while c not in b"\r\n":
s = s + c
c = self.fp.read(1)
self.char = self.fp.read(1)
# line endings can be 1 or 2 of \r \n, in either order
if self.char in b"\r\n":
self.char = None
return s.decode('latin-1')
def _accept(prefix):
return prefix[:4] == b"%!PS" or \
(len(prefix) >= 4 and i32(prefix) == 0xC6D3D0C5)
##
# Image plugin for Encapsulated Postscript. This plugin supports only
# a few variants of this format.
class EpsImageFile(ImageFile.ImageFile):
"""EPS File Parser for the Python Imaging Library"""
format = "EPS"
format_description = "Encapsulated Postscript"
mode_map = {1: "L", 2: "LAB", 3: "RGB", 4: "CMYK"}
def _open(self):
(length, offset) = self._find_offset(self.fp)
# Rewrap the open file pointer in something that will
# convert line endings and decode to latin-1.
fp = PSFile(self.fp)
# go to offset - start of "%!PS"
fp.seek(offset)
box = None
self.mode = "RGB"
self._size = 1, 1 # FIXME: huh?
#
# Load EPS header
s_raw = fp.readline()
s = s_raw.strip('\r\n')
while s_raw:
if s:
if len(s) > 255:
raise SyntaxError("not an EPS file")
try:
m = split.match(s)
except re.error:
raise SyntaxError("not an EPS file")
if m:
k, v = m.group(1, 2)
self.info[k] = v
if k == "BoundingBox":
try:
# Note: The DSC spec says that BoundingBox
# fields should be integers, but some drivers
# put floating point values there anyway.
box = [int(float(i)) for i in v.split()]
self._size = box[2] - box[0], box[3] - box[1]
self.tile = [("eps", (0, 0) + self.size, offset,
(length, box))]
except Exception:
pass
else:
m = field.match(s)
if m:
k = m.group(1)
if k == "EndComments":
break
if k[:8] == "PS-Adobe":
self.info[k[:8]] = k[9:]
else:
self.info[k] = ""
elif s[0] == '%':
# handle non-DSC Postscript comments that some
# tools mistakenly put in the Comments section
pass
else:
raise IOError("bad EPS header")
s_raw = fp.readline()
s = s_raw.strip('\r\n')
if s and s[:1] != "%":
break
#
# Scan for an "ImageData" descriptor
while s[:1] == "%":
if len(s) > 255:
raise SyntaxError("not an EPS file")
if s[:11] == "%ImageData:":
# Encoded bitmapped image.
x, y, bi, mo = s[11:].split(None, 7)[:4]
if int(bi) != 8:
break
try:
self.mode = self.mode_map[int(mo)]
except ValueError:
break
self._size = int(x), int(y)
return
s = fp.readline().strip('\r\n')
if not s:
break
if not box:
raise IOError("cannot determine EPS bounding box")
def _find_offset(self, fp):
s = fp.read(160)
if s[:4] == b"%!PS":
# for HEAD without binary preview
fp.seek(0, 2)
length = fp.tell()
offset = 0
elif i32(s[0:4]) == 0xC6D3D0C5:
# FIX for: Some EPS file not handled correctly / issue #302
# EPS can contain binary data
# or start directly with latin coding
# more info see:
# https://web.archive.org/web/20160528181353/http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf
offset = i32(s[4:8])
length = i32(s[8:12])
else:
raise SyntaxError("not an EPS file")
return (length, offset)
def load(self, scale=1):
# Load EPS via Ghostscript
if not self.tile:
return
self.im = Ghostscript(self.tile, self.size, self.fp, scale)
self.mode = self.im.mode
self._size = self.im.size
self.tile = []
def load_seek(self, *args, **kwargs):
# we can't incrementally load, so force ImageFile.parser to
# use our custom load method by defining this method.
pass
#
# --------------------------------------------------------------------
def _save(im, fp, filename, eps=1):
"""EPS Writer for the Python Imaging Library."""
#
# make sure image data is available
im.load()
#
# determine postscript image mode
if im.mode == "L":
operator = (8, 1, "image")
elif im.mode == "RGB":
operator = (8, 3, "false 3 colorimage")
elif im.mode == "CMYK":
operator = (8, 4, "false 4 colorimage")
else:
raise ValueError("image mode is not supported")
base_fp = fp
wrapped_fp = False
if fp != sys.stdout:
if sys.version_info.major > 2:
fp = io.TextIOWrapper(fp, encoding='latin-1')
wrapped_fp = True
try:
if eps:
#
# write EPS header
fp.write("%!PS-Adobe-3.0 EPSF-3.0\n")
fp.write("%%Creator: PIL 0.1 EpsEncode\n")
# fp.write("%%CreationDate: %s"...)
fp.write("%%%%BoundingBox: 0 0 %d %d\n" % im.size)
fp.write("%%Pages: 1\n")
fp.write("%%EndComments\n")
fp.write("%%Page: 1 1\n")
fp.write("%%ImageData: %d %d " % im.size)
fp.write("%d %d 0 1 1 \"%s\"\n" % operator)
#
# image header
fp.write("gsave\n")
fp.write("10 dict begin\n")
fp.write("/buf %d string def\n" % (im.size[0] * operator[1]))
fp.write("%d %d scale\n" % im.size)
fp.write("%d %d 8\n" % im.size) # <= bits
fp.write("[%d 0 0 -%d 0 %d]\n" % (im.size[0], im.size[1], im.size[1]))
fp.write("{ currentfile buf readhexstring pop } bind\n")
fp.write(operator[2] + "\n")
if hasattr(fp, "flush"):
fp.flush()
ImageFile._save(im, base_fp, [("eps", (0, 0)+im.size, 0, None)])
fp.write("\n%%%%EndBinary\n")
fp.write("grestore end\n")
if hasattr(fp, "flush"):
fp.flush()
finally:
if wrapped_fp:
fp.detach()
#
# --------------------------------------------------------------------
Image.register_open(EpsImageFile.format, EpsImageFile, _accept)
Image.register_save(EpsImageFile.format, _save)
Image.register_extensions(EpsImageFile.format, [".ps", ".eps"])
Image.register_mime(EpsImageFile.format, "application/postscript")

View File

@ -0,0 +1,315 @@
#
# The Python Imaging Library.
# $Id$
#
# EXIF tags
#
# Copyright (c) 2003 by Secret Labs AB
#
# See the README file for information on usage and redistribution.
#
##
# This module provides constants and clear-text names for various
# well-known EXIF tags.
##
##
# Maps EXIF tags to tag names.
TAGS = {
# possibly incomplete
0x000b: "ProcessingSoftware",
0x00fe: "NewSubfileType",
0x00ff: "SubfileType",
0x0100: "ImageWidth",
0x0101: "ImageLength",
0x0102: "BitsPerSample",
0x0103: "Compression",
0x0106: "PhotometricInterpretation",
0x0107: "Thresholding",
0x0108: "CellWidth",
0x0109: "CellLength",
0x010a: "FillOrder",
0x010d: "DocumentName",
0x010e: "ImageDescription",
0x010f: "Make",
0x0110: "Model",
0x0111: "StripOffsets",
0x0112: "Orientation",
0x0115: "SamplesPerPixel",
0x0116: "RowsPerStrip",
0x0117: "StripByteCounts",
0x0118: "MinSampleValue",
0x0119: "MaxSampleValue",
0x011a: "XResolution",
0x011b: "YResolution",
0x011c: "PlanarConfiguration",
0x011d: "PageName",
0x0120: "FreeOffsets",
0x0121: "FreeByteCounts",
0x0122: "GrayResponseUnit",
0x0123: "GrayResponseCurve",
0x0124: "T4Options",
0x0125: "T6Options",
0x0128: "ResolutionUnit",
0x0129: "PageNumber",
0x012d: "TransferFunction",
0x0131: "Software",
0x0132: "DateTime",
0x013b: "Artist",
0x013c: "HostComputer",
0x013d: "Predictor",
0x013e: "WhitePoint",
0x013f: "PrimaryChromaticities",
0x0140: "ColorMap",
0x0141: "HalftoneHints",
0x0142: "TileWidth",
0x0143: "TileLength",
0x0144: "TileOffsets",
0x0145: "TileByteCounts",
0x014a: "SubIFDs",
0x014c: "InkSet",
0x014d: "InkNames",
0x014e: "NumberOfInks",
0x0150: "DotRange",
0x0151: "TargetPrinter",
0x0152: "ExtraSamples",
0x0153: "SampleFormat",
0x0154: "SMinSampleValue",
0x0155: "SMaxSampleValue",
0x0156: "TransferRange",
0x0157: "ClipPath",
0x0158: "XClipPathUnits",
0x0159: "YClipPathUnits",
0x015a: "Indexed",
0x015b: "JPEGTables",
0x015f: "OPIProxy",
0x0200: "JPEGProc",
0x0201: "JpegIFOffset",
0x0202: "JpegIFByteCount",
0x0203: "JpegRestartInterval",
0x0205: "JpegLosslessPredictors",
0x0206: "JpegPointTransforms",
0x0207: "JpegQTables",
0x0208: "JpegDCTables",
0x0209: "JpegACTables",
0x0211: "YCbCrCoefficients",
0x0212: "YCbCrSubSampling",
0x0213: "YCbCrPositioning",
0x0214: "ReferenceBlackWhite",
0x02bc: "XMLPacket",
0x1000: "RelatedImageFileFormat",
0x1001: "RelatedImageWidth",
0x1002: "RelatedImageLength",
0x4746: "Rating",
0x4749: "RatingPercent",
0x800d: "ImageID",
0x828d: "CFARepeatPatternDim",
0x828e: "CFAPattern",
0x828f: "BatteryLevel",
0x8298: "Copyright",
0x829a: "ExposureTime",
0x829d: "FNumber",
0x83bb: "IPTCNAA",
0x8649: "ImageResources",
0x8769: "ExifOffset",
0x8773: "InterColorProfile",
0x8822: "ExposureProgram",
0x8824: "SpectralSensitivity",
0x8825: "GPSInfo",
0x8827: "ISOSpeedRatings",
0x8828: "OECF",
0x8829: "Interlace",
0x882a: "TimeZoneOffset",
0x882b: "SelfTimerMode",
0x9000: "ExifVersion",
0x9003: "DateTimeOriginal",
0x9004: "DateTimeDigitized",
0x9101: "ComponentsConfiguration",
0x9102: "CompressedBitsPerPixel",
0x9201: "ShutterSpeedValue",
0x9202: "ApertureValue",
0x9203: "BrightnessValue",
0x9204: "ExposureBiasValue",
0x9205: "MaxApertureValue",
0x9206: "SubjectDistance",
0x9207: "MeteringMode",
0x9208: "LightSource",
0x9209: "Flash",
0x920a: "FocalLength",
0x920b: "FlashEnergy",
0x920c: "SpatialFrequencyResponse",
0x920d: "Noise",
0x9211: "ImageNumber",
0x9212: "SecurityClassification",
0x9213: "ImageHistory",
0x9214: "SubjectLocation",
0x9215: "ExposureIndex",
0x9216: "TIFF/EPStandardID",
0x927c: "MakerNote",
0x9286: "UserComment",
0x9290: "SubsecTime",
0x9291: "SubsecTimeOriginal",
0x9292: "SubsecTimeDigitized",
0x9c9b: "XPTitle",
0x9c9c: "XPComment",
0x9c9d: "XPAuthor",
0x9c9e: "XPKeywords",
0x9c9f: "XPSubject",
0xa000: "FlashPixVersion",
0xa001: "ColorSpace",
0xa002: "ExifImageWidth",
0xa003: "ExifImageHeight",
0xa004: "RelatedSoundFile",
0xa005: "ExifInteroperabilityOffset",
0xa20b: "FlashEnergy",
0xa20c: "SpatialFrequencyResponse",
0xa20e: "FocalPlaneXResolution",
0xa20f: "FocalPlaneYResolution",
0xa210: "FocalPlaneResolutionUnit",
0xa214: "SubjectLocation",
0xa215: "ExposureIndex",
0xa217: "SensingMethod",
0xa300: "FileSource",
0xa301: "SceneType",
0xa302: "CFAPattern",
0xa401: "CustomRendered",
0xa402: "ExposureMode",
0xa403: "WhiteBalance",
0xa404: "DigitalZoomRatio",
0xa405: "FocalLengthIn35mmFilm",
0xa406: "SceneCaptureType",
0xa407: "GainControl",
0xa408: "Contrast",
0xa409: "Saturation",
0xa40a: "Sharpness",
0xa40b: "DeviceSettingDescription",
0xa40c: "SubjectDistanceRange",
0xa420: "ImageUniqueID",
0xa430: "CameraOwnerName",
0xa431: "BodySerialNumber",
0xa432: "LensSpecification",
0xa433: "LensMake",
0xa434: "LensModel",
0xa435: "LensSerialNumber",
0xa500: "Gamma",
0xc4a5: "PrintImageMatching",
0xc612: "DNGVersion",
0xc613: "DNGBackwardVersion",
0xc614: "UniqueCameraModel",
0xc615: "LocalizedCameraModel",
0xc616: "CFAPlaneColor",
0xc617: "CFALayout",
0xc618: "LinearizationTable",
0xc619: "BlackLevelRepeatDim",
0xc61a: "BlackLevel",
0xc61b: "BlackLevelDeltaH",
0xc61c: "BlackLevelDeltaV",
0xc61d: "WhiteLevel",
0xc61e: "DefaultScale",
0xc61f: "DefaultCropOrigin",
0xc620: "DefaultCropSize",
0xc621: "ColorMatrix1",
0xc622: "ColorMatrix2",
0xc623: "CameraCalibration1",
0xc624: "CameraCalibration2",
0xc625: "ReductionMatrix1",
0xc626: "ReductionMatrix2",
0xc627: "AnalogBalance",
0xc628: "AsShotNeutral",
0xc629: "AsShotWhiteXY",
0xc62a: "BaselineExposure",
0xc62b: "BaselineNoise",
0xc62c: "BaselineSharpness",
0xc62d: "BayerGreenSplit",
0xc62e: "LinearResponseLimit",
0xc62f: "CameraSerialNumber",
0xc630: "LensInfo",
0xc631: "ChromaBlurRadius",
0xc632: "AntiAliasStrength",
0xc633: "ShadowScale",
0xc634: "DNGPrivateData",
0xc635: "MakerNoteSafety",
0xc65a: "CalibrationIlluminant1",
0xc65b: "CalibrationIlluminant2",
0xc65c: "BestQualityScale",
0xc65d: "RawDataUniqueID",
0xc68b: "OriginalRawFileName",
0xc68c: "OriginalRawFileData",
0xc68d: "ActiveArea",
0xc68e: "MaskedAreas",
0xc68f: "AsShotICCProfile",
0xc690: "AsShotPreProfileMatrix",
0xc691: "CurrentICCProfile",
0xc692: "CurrentPreProfileMatrix",
0xc6bf: "ColorimetricReference",
0xc6f3: "CameraCalibrationSignature",
0xc6f4: "ProfileCalibrationSignature",
0xc6f6: "AsShotProfileName",
0xc6f7: "NoiseReductionApplied",
0xc6f8: "ProfileName",
0xc6f9: "ProfileHueSatMapDims",
0xc6fa: "ProfileHueSatMapData1",
0xc6fb: "ProfileHueSatMapData2",
0xc6fc: "ProfileToneCurve",
0xc6fd: "ProfileEmbedPolicy",
0xc6fe: "ProfileCopyright",
0xc714: "ForwardMatrix1",
0xc715: "ForwardMatrix2",
0xc716: "PreviewApplicationName",
0xc717: "PreviewApplicationVersion",
0xc718: "PreviewSettingsName",
0xc719: "PreviewSettingsDigest",
0xc71a: "PreviewColorSpace",
0xc71b: "PreviewDateTime",
0xc71c: "RawImageDigest",
0xc71d: "OriginalRawFileDigest",
0xc71e: "SubTileBlockSize",
0xc71f: "RowInterleaveFactor",
0xc725: "ProfileLookTableDims",
0xc726: "ProfileLookTableData",
0xc740: "OpcodeList1",
0xc741: "OpcodeList2",
0xc74e: "OpcodeList3",
0xc761: "NoiseProfile"
}
##
# Maps EXIF GPS tags to tag names.
GPSTAGS = {
0: "GPSVersionID",
1: "GPSLatitudeRef",
2: "GPSLatitude",
3: "GPSLongitudeRef",
4: "GPSLongitude",
5: "GPSAltitudeRef",
6: "GPSAltitude",
7: "GPSTimeStamp",
8: "GPSSatellites",
9: "GPSStatus",
10: "GPSMeasureMode",
11: "GPSDOP",
12: "GPSSpeedRef",
13: "GPSSpeed",
14: "GPSTrackRef",
15: "GPSTrack",
16: "GPSImgDirectionRef",
17: "GPSImgDirection",
18: "GPSMapDatum",
19: "GPSDestLatitudeRef",
20: "GPSDestLatitude",
21: "GPSDestLongitudeRef",
22: "GPSDestLongitude",
23: "GPSDestBearingRef",
24: "GPSDestBearing",
25: "GPSDestDistanceRef",
26: "GPSDestDistance",
27: "GPSProcessingMethod",
28: "GPSAreaInformation",
29: "GPSDateStamp",
30: "GPSDifferential",
31: "GPSHPositioningError",
}

View File

@ -0,0 +1,75 @@
#
# The Python Imaging Library
# $Id$
#
# FITS stub adapter
#
# Copyright (c) 1998-2003 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
from . import Image, ImageFile
_handler = None
def register_handler(handler):
"""
Install application-specific FITS image handler.
:param handler: Handler object.
"""
global _handler
_handler = handler
# --------------------------------------------------------------------
# Image adapter
def _accept(prefix):
return prefix[:6] == b"SIMPLE"
class FITSStubImageFile(ImageFile.StubImageFile):
format = "FITS"
format_description = "FITS"
def _open(self):
offset = self.fp.tell()
if not _accept(self.fp.read(6)):
raise SyntaxError("Not a FITS file")
# FIXME: add more sanity checks here; mandatory header items
# include SIMPLE, BITPIX, NAXIS, etc.
self.fp.seek(offset)
# make something up
self.mode = "F"
self._size = 1, 1
loader = self._load()
if loader:
loader.open(self)
def _load(self):
return _handler
def _save(im, fp, filename):
if _handler is None or not hasattr("_handler", "save"):
raise IOError("FITS save handler not installed")
_handler.save(im, fp, filename)
# --------------------------------------------------------------------
# Registry
Image.register_open(FITSStubImageFile.format, FITSStubImageFile, _accept)
Image.register_save(FITSStubImageFile.format, _save)
Image.register_extensions(FITSStubImageFile.format, [".fit", ".fits"])

View File

@ -0,0 +1,175 @@
#
# The Python Imaging Library.
# $Id$
#
# FLI/FLC file handling.
#
# History:
# 95-09-01 fl Created
# 97-01-03 fl Fixed parser, setup decoder tile
# 98-07-15 fl Renamed offset attribute to avoid name clash
#
# Copyright (c) Secret Labs AB 1997-98.
# Copyright (c) Fredrik Lundh 1995-97.
#
# See the README file for information on usage and redistribution.
#
from . import Image, ImageFile, ImagePalette
from ._binary import i8, i16le as i16, i32le as i32, o8
__version__ = "0.2"
#
# decoder
def _accept(prefix):
return len(prefix) >= 6 and i16(prefix[4:6]) in [0xAF11, 0xAF12]
##
# Image plugin for the FLI/FLC animation format. Use the <b>seek</b>
# method to load individual frames.
class FliImageFile(ImageFile.ImageFile):
format = "FLI"
format_description = "Autodesk FLI/FLC Animation"
_close_exclusive_fp_after_loading = False
def _open(self):
# HEAD
s = self.fp.read(128)
magic = i16(s[4:6])
if not (magic in [0xAF11, 0xAF12] and
i16(s[14:16]) in [0, 3] and # flags
s[20:22] == b"\x00\x00"): # reserved
raise SyntaxError("not an FLI/FLC file")
# frames
self.__framecount = i16(s[6:8])
# image characteristics
self.mode = "P"
self._size = i16(s[8:10]), i16(s[10:12])
# animation speed
duration = i32(s[16:20])
if magic == 0xAF11:
duration = (duration * 1000) // 70
self.info["duration"] = duration
# look for palette
palette = [(a, a, a) for a in range(256)]
s = self.fp.read(16)
self.__offset = 128
if i16(s[4:6]) == 0xF100:
# prefix chunk; ignore it
self.__offset = self.__offset + i32(s)
s = self.fp.read(16)
if i16(s[4:6]) == 0xF1FA:
# look for palette chunk
s = self.fp.read(6)
if i16(s[4:6]) == 11:
self._palette(palette, 2)
elif i16(s[4:6]) == 4:
self._palette(palette, 0)
palette = [o8(r)+o8(g)+o8(b) for (r, g, b) in palette]
self.palette = ImagePalette.raw("RGB", b"".join(palette))
# set things up to decode first frame
self.__frame = -1
self.__fp = self.fp
self.__rewind = self.fp.tell()
self.seek(0)
def _palette(self, palette, shift):
# load palette
i = 0
for e in range(i16(self.fp.read(2))):
s = self.fp.read(2)
i = i + i8(s[0])
n = i8(s[1])
if n == 0:
n = 256
s = self.fp.read(n * 3)
for n in range(0, len(s), 3):
r = i8(s[n]) << shift
g = i8(s[n+1]) << shift
b = i8(s[n+2]) << shift
palette[i] = (r, g, b)
i += 1
@property
def n_frames(self):
return self.__framecount
@property
def is_animated(self):
return self.__framecount > 1
def seek(self, frame):
if not self._seek_check(frame):
return
if frame < self.__frame:
self._seek(0)
for f in range(self.__frame + 1, frame + 1):
self._seek(f)
def _seek(self, frame):
if frame == 0:
self.__frame = -1
self.__fp.seek(self.__rewind)
self.__offset = 128
else:
# ensure that the previous frame was loaded
self.load()
if frame != self.__frame + 1:
raise ValueError("cannot seek to frame %d" % frame)
self.__frame = frame
# move to next frame
self.fp = self.__fp
self.fp.seek(self.__offset)
s = self.fp.read(4)
if not s:
raise EOFError
framesize = i32(s)
self.decodermaxblock = framesize
self.tile = [("fli", (0, 0)+self.size, self.__offset, None)]
self.__offset += framesize
def tell(self):
return self.__frame
def _close__fp(self):
try:
if self.__fp != self.fp:
self.__fp.close()
except AttributeError:
pass
finally:
self.__fp = None
#
# registry
Image.register_open(FliImageFile.format, FliImageFile, _accept)
Image.register_extensions(FliImageFile.format, [".fli", ".flc"])

View File

@ -0,0 +1,113 @@
#
# The Python Imaging Library
# $Id$
#
# base class for raster font file parsers
#
# history:
# 1997-06-05 fl created
# 1997-08-19 fl restrict image width
#
# Copyright (c) 1997-1998 by Secret Labs AB
# Copyright (c) 1997-1998 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
from __future__ import print_function
import os
from . import Image, _binary
WIDTH = 800
def puti16(fp, values):
# write network order (big-endian) 16-bit sequence
for v in values:
if v < 0:
v += 65536
fp.write(_binary.o16be(v))
##
# Base class for raster font file handlers.
class FontFile(object):
bitmap = None
def __init__(self):
self.info = {}
self.glyph = [None] * 256
def __getitem__(self, ix):
return self.glyph[ix]
def compile(self):
"Create metrics and bitmap"
if self.bitmap:
return
# create bitmap large enough to hold all data
h = w = maxwidth = 0
lines = 1
for glyph in self:
if glyph:
d, dst, src, im = glyph
h = max(h, src[3] - src[1])
w = w + (src[2] - src[0])
if w > WIDTH:
lines += 1
w = (src[2] - src[0])
maxwidth = max(maxwidth, w)
xsize = maxwidth
ysize = lines * h
if xsize == 0 and ysize == 0:
return ""
self.ysize = h
# paste glyphs into bitmap
self.bitmap = Image.new("1", (xsize, ysize))
self.metrics = [None] * 256
x = y = 0
for i in range(256):
glyph = self[i]
if glyph:
d, dst, src, im = glyph
xx = src[2] - src[0]
# yy = src[3] - src[1]
x0, y0 = x, y
x = x + xx
if x > WIDTH:
x, y = 0, y + h
x0, y0 = x, y
x = xx
s = src[0] + x0, src[1] + y0, src[2] + x0, src[3] + y0
self.bitmap.paste(im.crop(src), s)
self.metrics[i] = d, dst, s
def save(self, filename):
"Save font"
self.compile()
# font data
self.bitmap.save(os.path.splitext(filename)[0] + ".pbm", "PNG")
# font metrics
with open(os.path.splitext(filename)[0] + ".pil", "wb") as fp:
fp.write(b"PILfont\n")
fp.write((";;;;;;%d;\n" % self.ysize).encode('ascii')) # HACK!!!
fp.write(b"DATA\n")
for id in range(256):
m = self.metrics[id]
if not m:
puti16(fp, [0] * 10)
else:
puti16(fp, m[0] + m[1] + m[2])

View File

@ -0,0 +1,225 @@
#
# THIS IS WORK IN PROGRESS
#
# The Python Imaging Library.
# $Id$
#
# FlashPix support for PIL
#
# History:
# 97-01-25 fl Created (reads uncompressed RGB images only)
#
# Copyright (c) Secret Labs AB 1997.
# Copyright (c) Fredrik Lundh 1997.
#
# See the README file for information on usage and redistribution.
#
from __future__ import print_function
from . import Image, ImageFile
from ._binary import i32le as i32, i8
import olefile
__version__ = "0.1"
# we map from colour field tuples to (mode, rawmode) descriptors
MODES = {
# opacity
(0x00007ffe): ("A", "L"),
# monochrome
(0x00010000,): ("L", "L"),
(0x00018000, 0x00017ffe): ("RGBA", "LA"),
# photo YCC
(0x00020000, 0x00020001, 0x00020002): ("RGB", "YCC;P"),
(0x00028000, 0x00028001, 0x00028002, 0x00027ffe): ("RGBA", "YCCA;P"),
# standard RGB (NIFRGB)
(0x00030000, 0x00030001, 0x00030002): ("RGB", "RGB"),
(0x00038000, 0x00038001, 0x00038002, 0x00037ffe): ("RGBA", "RGBA"),
}
#
# --------------------------------------------------------------------
def _accept(prefix):
return prefix[:8] == olefile.MAGIC
##
# Image plugin for the FlashPix images.
class FpxImageFile(ImageFile.ImageFile):
format = "FPX"
format_description = "FlashPix"
def _open(self):
#
# read the OLE directory and see if this is a likely
# to be a FlashPix file
try:
self.ole = olefile.OleFileIO(self.fp)
except IOError:
raise SyntaxError("not an FPX file; invalid OLE file")
if self.ole.root.clsid != "56616700-C154-11CE-8553-00AA00A1F95B":
raise SyntaxError("not an FPX file; bad root CLSID")
self._open_index(1)
def _open_index(self, index=1):
#
# get the Image Contents Property Set
prop = self.ole.getproperties([
"Data Object Store %06d" % index,
"\005Image Contents"
])
# size (highest resolution)
self._size = prop[0x1000002], prop[0x1000003]
size = max(self.size)
i = 1
while size > 64:
size = size / 2
i += 1
self.maxid = i - 1
# mode. instead of using a single field for this, flashpix
# requires you to specify the mode for each channel in each
# resolution subimage, and leaves it to the decoder to make
# sure that they all match. for now, we'll cheat and assume
# that this is always the case.
id = self.maxid << 16
s = prop[0x2000002 | id]
colors = []
for i in range(i32(s, 4)):
# note: for now, we ignore the "uncalibrated" flag
colors.append(i32(s, 8+i*4) & 0x7fffffff)
self.mode, self.rawmode = MODES[tuple(colors)]
# load JPEG tables, if any
self.jpeg = {}
for i in range(256):
id = 0x3000001 | (i << 16)
if id in prop:
self.jpeg[i] = prop[id]
self._open_subimage(1, self.maxid)
def _open_subimage(self, index=1, subimage=0):
#
# setup tile descriptors for a given subimage
stream = [
"Data Object Store %06d" % index,
"Resolution %04d" % subimage,
"Subimage 0000 Header"
]
fp = self.ole.openstream(stream)
# skip prefix
fp.read(28)
# header stream
s = fp.read(36)
size = i32(s, 4), i32(s, 8)
# tilecount = i32(s, 12)
tilesize = i32(s, 16), i32(s, 20)
# channels = i32(s, 24)
offset = i32(s, 28)
length = i32(s, 32)
if size != self.size:
raise IOError("subimage mismatch")
# get tile descriptors
fp.seek(28 + offset)
s = fp.read(i32(s, 12) * length)
x = y = 0
xsize, ysize = size
xtile, ytile = tilesize
self.tile = []
for i in range(0, len(s), length):
compression = i32(s, i+8)
if compression == 0:
self.tile.append(("raw", (x, y, x+xtile, y+ytile),
i32(s, i) + 28, (self.rawmode)))
elif compression == 1:
# FIXME: the fill decoder is not implemented
self.tile.append(("fill", (x, y, x+xtile, y+ytile),
i32(s, i) + 28, (self.rawmode, s[12:16])))
elif compression == 2:
internal_color_conversion = i8(s[14])
jpeg_tables = i8(s[15])
rawmode = self.rawmode
if internal_color_conversion:
# The image is stored as usual (usually YCbCr).
if rawmode == "RGBA":
# For "RGBA", data is stored as YCbCrA based on
# negative RGB. The following trick works around
# this problem :
jpegmode, rawmode = "YCbCrK", "CMYK"
else:
jpegmode = None # let the decoder decide
else:
# The image is stored as defined by rawmode
jpegmode = rawmode
self.tile.append(("jpeg", (x, y, x+xtile, y+ytile),
i32(s, i) + 28, (rawmode, jpegmode)))
# FIXME: jpeg tables are tile dependent; the prefix
# data must be placed in the tile descriptor itself!
if jpeg_tables:
self.tile_prefix = self.jpeg[jpeg_tables]
else:
raise IOError("unknown/invalid compression")
x = x + xtile
if x >= xsize:
x, y = 0, y + ytile
if y >= ysize:
break # isn't really required
self.stream = stream
self.fp = None
def load(self):
if not self.fp:
self.fp = self.ole.openstream(self.stream[:2] +
["Subimage 0000 Data"])
return ImageFile.ImageFile.load(self)
#
# --------------------------------------------------------------------
Image.register_open(FpxImageFile.format, FpxImageFile, _accept)
Image.register_extension(FpxImageFile.format, ".fpx")

View File

@ -0,0 +1,107 @@
"""
A Pillow loader for .ftc and .ftu files (FTEX)
Jerome Leclanche <jerome@leclan.ch>
The contents of this file are hereby released in the public domain (CC0)
Full text of the CC0 license:
https://creativecommons.org/publicdomain/zero/1.0/
Independence War 2: Edge Of Chaos - Texture File Format - 16 October 2001
The textures used for 3D objects in Independence War 2: Edge Of Chaos are in a
packed custom format called FTEX. This file format uses file extensions FTC
and FTU.
* FTC files are compressed textures (using standard texture compression).
* FTU files are not compressed.
Texture File Format
The FTC and FTU texture files both use the same format. This
has the following structure:
{header}
{format_directory}
{data}
Where:
{header} = {
u32:magic,
u32:version,
u32:width,
u32:height,
u32:mipmap_count,
u32:format_count
}
* The "magic" number is "FTEX".
* "width" and "height" are the dimensions of the texture.
* "mipmap_count" is the number of mipmaps in the texture.
* "format_count" is the number of texture formats (different versions of the
same texture) in this file.
{format_directory} = format_count * { u32:format, u32:where }
The format value is 0 for DXT1 compressed textures and 1 for 24-bit RGB
uncompressed textures.
The texture data for a format starts at the position "where" in the file.
Each set of texture data in the file has the following structure:
{data} = format_count * { u32:mipmap_size, mipmap_size * { u8 } }
* "mipmap_size" is the number of bytes in that mip level. For compressed
textures this is the size of the texture data compressed with DXT1. For 24 bit
uncompressed textures, this is 3 * width * height. Following this are the image
bytes for that mipmap level.
Note: All data is stored in little-Endian (Intel) byte order.
"""
import struct
from io import BytesIO
from . import Image, ImageFile
MAGIC = b"FTEX"
FORMAT_DXT1 = 0
FORMAT_UNCOMPRESSED = 1
class FtexImageFile(ImageFile.ImageFile):
format = "FTEX"
format_description = "Texture File Format (IW2:EOC)"
def _open(self):
struct.unpack("<I", self.fp.read(4)) # magic
struct.unpack("<i", self.fp.read(4)) # version
self._size = struct.unpack("<2i", self.fp.read(8))
mipmap_count, format_count = struct.unpack("<2i", self.fp.read(8))
self.mode = "RGB"
# Only support single-format files.
# I don't know of any multi-format file.
assert format_count == 1
format, where = struct.unpack("<2i", self.fp.read(8))
self.fp.seek(where)
mipmap_size, = struct.unpack("<i", self.fp.read(4))
data = self.fp.read(mipmap_size)
if format == FORMAT_DXT1:
self.mode = "RGBA"
self.tile = [("bcn", (0, 0) + self.size, 0, (1))]
elif format == FORMAT_UNCOMPRESSED:
self.tile = [("raw", (0, 0) + self.size, 0, ('RGB', 0, 1))]
else:
raise ValueError(
"Invalid texture compression format: %r" % (format))
self.fp.close()
self.fp = BytesIO(data)
def load_seek(self, pos):
pass
def _validate(prefix):
return prefix[:4] == MAGIC
Image.register_open(FtexImageFile.format, FtexImageFile, _validate)
Image.register_extensions(FtexImageFile.format, [".ftc", ".ftu"])

View File

@ -0,0 +1,96 @@
#
# The Python Imaging Library
#
# load a GIMP brush file
#
# History:
# 96-03-14 fl Created
# 16-01-08 es Version 2
#
# Copyright (c) Secret Labs AB 1997.
# Copyright (c) Fredrik Lundh 1996.
# Copyright (c) Eric Soroos 2016.
#
# See the README file for information on usage and redistribution.
#
#
# See https://github.com/GNOME/gimp/blob/master/devel-docs/gbr.txt for
# format documentation.
#
# This code Interprets version 1 and 2 .gbr files.
# Version 1 files are obsolete, and should not be used for new
# brushes.
# Version 2 files are saved by GIMP v2.8 (at least)
# Version 3 files have a format specifier of 18 for 16bit floats in
# the color depth field. This is currently unsupported by Pillow.
from . import Image, ImageFile
from ._binary import i32be as i32
def _accept(prefix):
return len(prefix) >= 8 and \
i32(prefix[:4]) >= 20 and i32(prefix[4:8]) in (1, 2)
##
# Image plugin for the GIMP brush format.
class GbrImageFile(ImageFile.ImageFile):
format = "GBR"
format_description = "GIMP brush file"
def _open(self):
header_size = i32(self.fp.read(4))
version = i32(self.fp.read(4))
if header_size < 20:
raise SyntaxError("not a GIMP brush")
if version not in (1, 2):
raise SyntaxError("Unsupported GIMP brush version: %s" % version)
width = i32(self.fp.read(4))
height = i32(self.fp.read(4))
color_depth = i32(self.fp.read(4))
if width <= 0 or height <= 0:
raise SyntaxError("not a GIMP brush")
if color_depth not in (1, 4):
raise SyntaxError(
"Unsupported GIMP brush color depth: %s" % color_depth)
if version == 1:
comment_length = header_size-20
else:
comment_length = header_size-28
magic_number = self.fp.read(4)
if magic_number != b'GIMP':
raise SyntaxError("not a GIMP brush, bad magic number")
self.info['spacing'] = i32(self.fp.read(4))
comment = self.fp.read(comment_length)[:-1]
if color_depth == 1:
self.mode = "L"
else:
self.mode = 'RGBA'
self._size = width, height
self.info["comment"] = comment
# Image might not be small
Image._decompression_bomb_check(self.size)
# Data is an uncompressed block of w * h * bytes/pixel
self._data_size = width * height * color_depth
def load(self):
self.im = Image.core.new(self.mode, self.size)
self.frombytes(self.fp.read(self._data_size))
#
# registry
Image.register_open(GbrImageFile.format, GbrImageFile, _accept)
Image.register_extension(GbrImageFile.format, ".gbr")

View File

@ -0,0 +1,85 @@
#
# The Python Imaging Library.
# $Id$
#
# GD file handling
#
# History:
# 1996-04-12 fl Created
#
# Copyright (c) 1997 by Secret Labs AB.
# Copyright (c) 1996 by Fredrik Lundh.
#
# See the README file for information on usage and redistribution.
#
# NOTE: This format cannot be automatically recognized, so the
# class is not registered for use with Image.open(). To open a
# gd file, use the GdImageFile.open() function instead.
# THE GD FORMAT IS NOT DESIGNED FOR DATA INTERCHANGE. This
# implementation is provided for convenience and demonstrational
# purposes only.
from . import ImageFile, ImagePalette
from ._binary import i8, i16be as i16, i32be as i32
__version__ = "0.1"
##
# Image plugin for the GD uncompressed format. Note that this format
# is not supported by the standard <b>Image.open</b> function. To use
# this plugin, you have to import the <b>GdImageFile</b> module and
# use the <b>GdImageFile.open</b> function.
class GdImageFile(ImageFile.ImageFile):
format = "GD"
format_description = "GD uncompressed images"
def _open(self):
# Header
s = self.fp.read(1037)
if not i16(s[:2]) in [65534, 65535]:
raise SyntaxError("Not a valid GD 2.x .gd file")
self.mode = "L" # FIXME: "P"
self._size = i16(s[2:4]), i16(s[4:6])
trueColor = i8(s[6])
trueColorOffset = 2 if trueColor else 0
# transparency index
tindex = i32(s[7+trueColorOffset:7+trueColorOffset+4])
if tindex < 256:
self.info["transparency"] = tindex
self.palette = ImagePalette.raw(
"XBGR", s[7+trueColorOffset+4:7+trueColorOffset+4+256*4])
self.tile = [("raw", (0, 0)+self.size, 7+trueColorOffset+4+256*4,
("L", 0, 1))]
def open(fp, mode="r"):
"""
Load texture from a GD image file.
:param filename: GD file name, or an opened file handle.
:param mode: Optional mode. In this version, if the mode argument
is given, it must be "r".
:returns: An image instance.
:raises IOError: If the image could not be read.
"""
if mode != "r":
raise ValueError("bad mode")
try:
return GdImageFile(fp)
except SyntaxError:
raise IOError("cannot identify this image file")

View File

@ -0,0 +1,852 @@
#
# The Python Imaging Library.
# $Id$
#
# GIF file handling
#
# History:
# 1995-09-01 fl Created
# 1996-12-14 fl Added interlace support
# 1996-12-30 fl Added animation support
# 1997-01-05 fl Added write support, fixed local colour map bug
# 1997-02-23 fl Make sure to load raster data in getdata()
# 1997-07-05 fl Support external decoder (0.4)
# 1998-07-09 fl Handle all modes when saving (0.5)
# 1998-07-15 fl Renamed offset attribute to avoid name clash
# 2001-04-16 fl Added rewind support (seek to frame 0) (0.6)
# 2001-04-17 fl Added palette optimization (0.7)
# 2002-06-06 fl Added transparency support for save (0.8)
# 2004-02-24 fl Disable interlacing for small images
#
# Copyright (c) 1997-2004 by Secret Labs AB
# Copyright (c) 1995-2004 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
from . import Image, ImageFile, ImagePalette, ImageChops, ImageSequence
from ._binary import i8, i16le as i16, o8, o16le as o16
import itertools
__version__ = "0.9"
# --------------------------------------------------------------------
# Identify/read GIF files
def _accept(prefix):
return prefix[:6] in [b"GIF87a", b"GIF89a"]
##
# Image plugin for GIF images. This plugin supports both GIF87 and
# GIF89 images.
class GifImageFile(ImageFile.ImageFile):
format = "GIF"
format_description = "Compuserve GIF"
_close_exclusive_fp_after_loading = False
global_palette = None
def data(self):
s = self.fp.read(1)
if s and i8(s):
return self.fp.read(i8(s))
return None
def _open(self):
# Screen
s = self.fp.read(13)
if s[:6] not in [b"GIF87a", b"GIF89a"]:
raise SyntaxError("not a GIF file")
self.info["version"] = s[:6]
self._size = i16(s[6:]), i16(s[8:])
self.tile = []
flags = i8(s[10])
bits = (flags & 7) + 1
if flags & 128:
# get global palette
self.info["background"] = i8(s[11])
# check if palette contains colour indices
p = self.fp.read(3 << bits)
for i in range(0, len(p), 3):
if not (i//3 == i8(p[i]) == i8(p[i+1]) == i8(p[i+2])):
p = ImagePalette.raw("RGB", p)
self.global_palette = self.palette = p
break
self.__fp = self.fp # FIXME: hack
self.__rewind = self.fp.tell()
self._n_frames = None
self._is_animated = None
self._seek(0) # get ready to read first frame
@property
def n_frames(self):
if self._n_frames is None:
current = self.tell()
try:
while True:
self.seek(self.tell() + 1)
except EOFError:
self._n_frames = self.tell() + 1
self.seek(current)
return self._n_frames
@property
def is_animated(self):
if self._is_animated is None:
if self._n_frames is not None:
self._is_animated = self._n_frames != 1
else:
current = self.tell()
try:
self.seek(1)
self._is_animated = True
except EOFError:
self._is_animated = False
self.seek(current)
return self._is_animated
def seek(self, frame):
if not self._seek_check(frame):
return
if frame < self.__frame:
self._seek(0)
last_frame = self.__frame
for f in range(self.__frame + 1, frame + 1):
try:
self._seek(f)
except EOFError:
self.seek(last_frame)
raise EOFError("no more images in GIF file")
def _seek(self, frame):
if frame == 0:
# rewind
self.__offset = 0
self.dispose = None
self.dispose_extent = [0, 0, 0, 0] # x0, y0, x1, y1
self.__frame = -1
self.__fp.seek(self.__rewind)
self._prev_im = None
self.disposal_method = 0
else:
# ensure that the previous frame was loaded
if not self.im:
self.load()
if frame != self.__frame + 1:
raise ValueError("cannot seek to frame %d" % frame)
self.__frame = frame
self.tile = []
self.fp = self.__fp
if self.__offset:
# backup to last frame
self.fp.seek(self.__offset)
while self.data():
pass
self.__offset = 0
if self.dispose:
self.im.paste(self.dispose, self.dispose_extent)
from copy import copy
self.palette = copy(self.global_palette)
info = {}
while True:
s = self.fp.read(1)
if not s or s == b";":
break
elif s == b"!":
#
# extensions
#
s = self.fp.read(1)
block = self.data()
if i8(s) == 249:
#
# graphic control extension
#
flags = i8(block[0])
if flags & 1:
info["transparency"] = i8(block[3])
info["duration"] = i16(block[1:3]) * 10
# disposal method - find the value of bits 4 - 6
dispose_bits = 0b00011100 & flags
dispose_bits = dispose_bits >> 2
if dispose_bits:
# only set the dispose if it is not
# unspecified. I'm not sure if this is
# correct, but it seems to prevent the last
# frame from looking odd for some animations
self.disposal_method = dispose_bits
elif i8(s) == 254:
#
# comment extension
#
while block:
if "comment" in info:
info["comment"] += block
else:
info["comment"] = block
block = self.data()
continue
elif i8(s) == 255:
#
# application extension
#
info["extension"] = block, self.fp.tell()
if block[:11] == b"NETSCAPE2.0":
block = self.data()
if len(block) >= 3 and i8(block[0]) == 1:
info["loop"] = i16(block[1:3])
while self.data():
pass
elif s == b",":
#
# local image
#
s = self.fp.read(9)
# extent
x0, y0 = i16(s[0:]), i16(s[2:])
x1, y1 = x0 + i16(s[4:]), y0 + i16(s[6:])
self.dispose_extent = x0, y0, x1, y1
flags = i8(s[8])
interlace = (flags & 64) != 0
if flags & 128:
bits = (flags & 7) + 1
self.palette =\
ImagePalette.raw("RGB", self.fp.read(3 << bits))
# image data
bits = i8(self.fp.read(1))
self.__offset = self.fp.tell()
self.tile = [("gif",
(x0, y0, x1, y1),
self.__offset,
(bits, interlace))]
break
else:
pass
# raise IOError, "illegal GIF tag `%x`" % i8(s)
try:
if self.disposal_method < 2:
# do not dispose or none specified
self.dispose = None
elif self.disposal_method == 2:
# replace with background colour
self.dispose = Image.core.fill("P", self.size,
self.info["background"])
else:
# replace with previous contents
if self.im:
self.dispose = self.im.copy()
# only dispose the extent in this frame
if self.dispose:
self.dispose = self._crop(self.dispose, self.dispose_extent)
except (AttributeError, KeyError):
pass
if not self.tile:
# self.__fp = None
raise EOFError
for k in ["transparency", "duration", "comment", "extension", "loop"]:
if k in info:
self.info[k] = info[k]
elif k in self.info:
del self.info[k]
self.mode = "L"
if self.palette:
self.mode = "P"
def tell(self):
return self.__frame
def load_end(self):
ImageFile.ImageFile.load_end(self)
# if the disposal method is 'do not dispose', transparent
# pixels should show the content of the previous frame
if self._prev_im and self.disposal_method == 1:
# we do this by pasting the updated area onto the previous
# frame which we then use as the current image content
updated = self._crop(self.im, self.dispose_extent)
self._prev_im.paste(updated, self.dispose_extent,
updated.convert('RGBA'))
self.im = self._prev_im
self._prev_im = self.im.copy()
def _close__fp(self):
try:
if self.__fp != self.fp:
self.__fp.close()
except AttributeError:
pass
finally:
self.__fp = None
# --------------------------------------------------------------------
# Write GIF files
RAWMODE = {
"1": "L",
"L": "L",
"P": "P"
}
def _normalize_mode(im, initial_call=False):
"""
Takes an image (or frame), returns an image in a mode that is appropriate
for saving in a Gif.
It may return the original image, or it may return an image converted to
palette or 'L' mode.
UNDONE: What is the point of mucking with the initial call palette, for
an image that shouldn't have a palette, or it would be a mode 'P' and
get returned in the RAWMODE clause.
:param im: Image object
:param initial_call: Default false, set to true for a single frame.
:returns: Image object
"""
if im.mode in RAWMODE:
im.load()
return im
if Image.getmodebase(im.mode) == "RGB":
if initial_call:
palette_size = 256
if im.palette:
palette_size = len(im.palette.getdata()[1]) // 3
return im.convert("P", palette=Image.ADAPTIVE, colors=palette_size)
else:
return im.convert("P")
return im.convert("L")
def _normalize_palette(im, palette, info):
"""
Normalizes the palette for image.
- Sets the palette to the incoming palette, if provided.
- Ensures that there's a palette for L mode images
- Optimizes the palette if necessary/desired.
:param im: Image object
:param palette: bytes object containing the source palette, or ....
:param info: encoderinfo
:returns: Image object
"""
source_palette = None
if palette:
# a bytes palette
if isinstance(palette, (bytes, bytearray, list)):
source_palette = bytearray(palette[:768])
if isinstance(palette, ImagePalette.ImagePalette):
source_palette = bytearray(itertools.chain.from_iterable(
zip(palette.palette[:256],
palette.palette[256:512],
palette.palette[512:768])))
if im.mode == "P":
if not source_palette:
source_palette = im.im.getpalette("RGB")[:768]
else: # L-mode
if not source_palette:
source_palette = bytearray(i//3 for i in range(768))
im.palette = ImagePalette.ImagePalette("RGB",
palette=source_palette)
used_palette_colors = _get_optimize(im, info)
if used_palette_colors is not None:
return im.remap_palette(used_palette_colors, source_palette)
im.palette.palette = source_palette
return im
def _write_single_frame(im, fp, palette):
im_out = _normalize_mode(im, True)
for k, v in im_out.info.items():
im.encoderinfo.setdefault(k, v)
im_out = _normalize_palette(im_out, palette, im.encoderinfo)
for s in _get_global_header(im_out, im.encoderinfo):
fp.write(s)
# local image header
flags = 0
if get_interlace(im):
flags = flags | 64
_write_local_header(fp, im, (0, 0), flags)
im_out.encoderconfig = (8, get_interlace(im))
ImageFile._save(im_out, fp, [("gif", (0, 0)+im.size, 0,
RAWMODE[im_out.mode])])
fp.write(b"\0") # end of image data
def _write_multiple_frames(im, fp, palette):
duration = im.encoderinfo.get("duration", im.info.get("duration"))
disposal = im.encoderinfo.get("disposal", im.info.get("disposal"))
im_frames = []
frame_count = 0
for imSequence in itertools.chain([im],
im.encoderinfo.get("append_images", [])):
for im_frame in ImageSequence.Iterator(imSequence):
# a copy is required here since seek can still mutate the image
im_frame = _normalize_mode(im_frame.copy())
if frame_count == 0:
for k, v in im_frame.info.items():
im.encoderinfo.setdefault(k, v)
im_frame = _normalize_palette(im_frame, palette, im.encoderinfo)
encoderinfo = im.encoderinfo.copy()
if isinstance(duration, (list, tuple)):
encoderinfo['duration'] = duration[frame_count]
if isinstance(disposal, (list, tuple)):
encoderinfo["disposal"] = disposal[frame_count]
frame_count += 1
if im_frames:
# delta frame
previous = im_frames[-1]
if _get_palette_bytes(im_frame) == \
_get_palette_bytes(previous['im']):
delta = ImageChops.subtract_modulo(im_frame,
previous['im'])
else:
delta = ImageChops.subtract_modulo(
im_frame.convert('RGB'), previous['im'].convert('RGB'))
bbox = delta.getbbox()
if not bbox:
# This frame is identical to the previous frame
if duration:
previous['encoderinfo']['duration'] += \
encoderinfo['duration']
continue
else:
bbox = None
im_frames.append({
'im': im_frame,
'bbox': bbox,
'encoderinfo': encoderinfo
})
if len(im_frames) > 1:
for frame_data in im_frames:
im_frame = frame_data['im']
if not frame_data['bbox']:
# global header
for s in _get_global_header(im_frame,
frame_data['encoderinfo']):
fp.write(s)
offset = (0, 0)
else:
# compress difference
frame_data['encoderinfo']['include_color_table'] = True
im_frame = im_frame.crop(frame_data['bbox'])
offset = frame_data['bbox'][:2]
_write_frame_data(fp, im_frame, offset, frame_data['encoderinfo'])
return True
def _save_all(im, fp, filename):
_save(im, fp, filename, save_all=True)
def _save(im, fp, filename, save_all=False):
# header
if "palette" in im.encoderinfo or "palette" in im.info:
palette = im.encoderinfo.get("palette", im.info.get("palette"))
else:
palette = None
im.encoderinfo["optimize"] = im.encoderinfo.get("optimize", True)
if not save_all or not _write_multiple_frames(im, fp, palette):
_write_single_frame(im, fp, palette)
fp.write(b";") # end of file
if hasattr(fp, "flush"):
fp.flush()
def get_interlace(im):
interlace = im.encoderinfo.get("interlace", 1)
# workaround for @PIL153
if min(im.size) < 16:
interlace = 0
return interlace
def _write_local_header(fp, im, offset, flags):
transparent_color_exists = False
try:
transparency = im.encoderinfo["transparency"]
except KeyError:
pass
else:
transparency = int(transparency)
# optimize the block away if transparent color is not used
transparent_color_exists = True
used_palette_colors = _get_optimize(im, im.encoderinfo)
if used_palette_colors is not None:
# adjust the transparency index after optimize
try:
transparency = used_palette_colors.index(transparency)
except ValueError:
transparent_color_exists = False
if "duration" in im.encoderinfo:
duration = int(im.encoderinfo["duration"] / 10)
else:
duration = 0
disposal = int(im.encoderinfo.get('disposal', 0))
if transparent_color_exists or duration != 0 or disposal:
packed_flag = 1 if transparent_color_exists else 0
packed_flag |= disposal << 2
if not transparent_color_exists:
transparency = 0
fp.write(b"!" +
o8(249) + # extension intro
o8(4) + # length
o8(packed_flag) + # packed fields
o16(duration) + # duration
o8(transparency) + # transparency index
o8(0))
if "comment" in im.encoderinfo and \
1 <= len(im.encoderinfo["comment"]):
fp.write(b"!" +
o8(254)) # extension intro
for i in range(0, len(im.encoderinfo["comment"]), 255):
subblock = im.encoderinfo["comment"][i:i+255]
fp.write(o8(len(subblock)) +
subblock)
fp.write(o8(0))
if "loop" in im.encoderinfo:
number_of_loops = im.encoderinfo["loop"]
fp.write(b"!" +
o8(255) + # extension intro
o8(11) +
b"NETSCAPE2.0" +
o8(3) +
o8(1) +
o16(number_of_loops) + # number of loops
o8(0))
include_color_table = im.encoderinfo.get('include_color_table')
if include_color_table:
palette_bytes = _get_palette_bytes(im)
color_table_size = _get_color_table_size(palette_bytes)
if color_table_size:
flags = flags | 128 # local color table flag
flags = flags | color_table_size
fp.write(b"," +
o16(offset[0]) + # offset
o16(offset[1]) +
o16(im.size[0]) + # size
o16(im.size[1]) +
o8(flags)) # flags
if include_color_table and color_table_size:
fp.write(_get_header_palette(palette_bytes))
fp.write(o8(8)) # bits
def _save_netpbm(im, fp, filename):
# Unused by default.
# To use, uncomment the register_save call at the end of the file.
#
# If you need real GIF compression and/or RGB quantization, you
# can use the external NETPBM/PBMPLUS utilities. See comments
# below for information on how to enable this.
import os
from subprocess import Popen, check_call, PIPE, CalledProcessError
file = im._dump()
with open(filename, 'wb') as f:
if im.mode != "RGB":
with open(os.devnull, 'wb') as devnull:
check_call(["ppmtogif", file], stdout=f, stderr=devnull)
else:
# Pipe ppmquant output into ppmtogif
# "ppmquant 256 %s | ppmtogif > %s" % (file, filename)
quant_cmd = ["ppmquant", "256", file]
togif_cmd = ["ppmtogif"]
with open(os.devnull, 'wb') as devnull:
quant_proc = Popen(quant_cmd, stdout=PIPE, stderr=devnull)
togif_proc = Popen(togif_cmd, stdin=quant_proc.stdout,
stdout=f, stderr=devnull)
# Allow ppmquant to receive SIGPIPE if ppmtogif exits
quant_proc.stdout.close()
retcode = quant_proc.wait()
if retcode:
raise CalledProcessError(retcode, quant_cmd)
retcode = togif_proc.wait()
if retcode:
raise CalledProcessError(retcode, togif_cmd)
try:
os.unlink(file)
except OSError:
pass
# Force optimization so that we can test performance against
# cases where it took lots of memory and time previously.
_FORCE_OPTIMIZE = False
def _get_optimize(im, info):
"""
Palette optimization is a potentially expensive operation.
This function determines if the palette should be optimized using
some heuristics, then returns the list of palette entries in use.
:param im: Image object
:param info: encoderinfo
:returns: list of indexes of palette entries in use, or None
"""
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
optimise = _FORCE_OPTIMIZE or im.mode == 'L'
if optimise or im.width * im.height < 512 * 512:
# check which colors are used
used_palette_colors = []
for i, count in enumerate(im.histogram()):
if count:
used_palette_colors.append(i)
if optimise or (len(used_palette_colors) <= 128 and
max(used_palette_colors) > len(used_palette_colors)):
return used_palette_colors
def _get_color_table_size(palette_bytes):
# calculate the palette size for the header
import math
color_table_size = int(math.ceil(math.log(len(palette_bytes)//3, 2)))-1
if color_table_size < 0:
color_table_size = 0
return color_table_size
def _get_header_palette(palette_bytes):
"""
Returns the palette, null padded to the next power of 2 (*3) bytes
suitable for direct inclusion in the GIF header
:param palette_bytes: Unpadded palette bytes, in RGBRGB form
:returns: Null padded palette
"""
color_table_size = _get_color_table_size(palette_bytes)
# add the missing amount of bytes
# the palette has to be 2<<n in size
actual_target_size_diff = (2 << color_table_size) - len(palette_bytes)//3
if actual_target_size_diff > 0:
palette_bytes += o8(0) * 3 * actual_target_size_diff
return palette_bytes
def _get_palette_bytes(im):
"""
Gets the palette for inclusion in the gif header
:param im: Image object
:returns: Bytes, len<=768 suitable for inclusion in gif header
"""
return im.palette.palette
def _get_global_header(im, info):
"""Return a list of strings representing a GIF header"""
# Header Block
# http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp
version = b"87a"
for extensionKey in ["transparency", "duration", "loop", "comment"]:
if info and extensionKey in info:
if ((extensionKey == "duration" and info[extensionKey] == 0) or
(extensionKey == "comment" and
not (1 <= len(info[extensionKey]) <= 255))):
continue
version = b"89a"
break
else:
if im.info.get("version") == b"89a":
version = b"89a"
background = 0
if "background" in info:
background = info["background"]
if isinstance(background, tuple):
# WebPImagePlugin stores an RGBA value in info["background"]
# So it must be converted to the same format as GifImagePlugin's
# info["background"] - a global color table index
background = im.palette.getcolor(background)
palette_bytes = _get_palette_bytes(im)
color_table_size = _get_color_table_size(palette_bytes)
return [
b"GIF"+version + # signature + version
o16(im.size[0]) + # canvas width
o16(im.size[1]), # canvas height
# Logical Screen Descriptor
# size of global color table + global color table flag
o8(color_table_size + 128), # packed fields
# background + reserved/aspect
o8(background) + o8(0),
# Global Color Table
_get_header_palette(palette_bytes)
]
def _write_frame_data(fp, im_frame, offset, params):
try:
im_frame.encoderinfo = params
# local image header
_write_local_header(fp, im_frame, offset, 0)
ImageFile._save(im_frame, fp, [("gif", (0, 0)+im_frame.size, 0,
RAWMODE[im_frame.mode])])
fp.write(b"\0") # end of image data
finally:
del im_frame.encoderinfo
# --------------------------------------------------------------------
# Legacy GIF utilities
def getheader(im, palette=None, info=None):
"""
Legacy Method to get Gif data from image.
Warning:: May modify image data.
:param im: Image object
:param palette: bytes object containing the source palette, or ....
:param info: encoderinfo
:returns: tuple of(list of header items, optimized palette)
"""
used_palette_colors = _get_optimize(im, info)
if info is None:
info = {}
if "background" not in info and "background" in im.info:
info["background"] = im.info["background"]
im_mod = _normalize_palette(im, palette, info)
im.palette = im_mod.palette
im.im = im_mod.im
header = _get_global_header(im, info)
return header, used_palette_colors
# To specify duration, add the time in milliseconds to getdata(),
# e.g. getdata(im_frame, duration=1000)
def getdata(im, offset=(0, 0), **params):
"""
Legacy Method
Return a list of strings representing this image.
The first string is a local image header, the rest contains
encoded image data.
:param im: Image object
:param offset: Tuple of (x, y) pixels. Defaults to (0,0)
:param \\**params: E.g. duration or other encoder info parameters
:returns: List of Bytes containing gif encoded frame data
"""
class Collector(object):
data = []
def write(self, data):
self.data.append(data)
im.load() # make sure raster data is available
fp = Collector()
_write_frame_data(fp, im, offset, params)
return fp.data
# --------------------------------------------------------------------
# Registry
Image.register_open(GifImageFile.format, GifImageFile, _accept)
Image.register_save(GifImageFile.format, _save)
Image.register_save_all(GifImageFile.format, _save_all)
Image.register_extension(GifImageFile.format, ".gif")
Image.register_mime(GifImageFile.format, "image/gif")
#
# Uncomment the following line if you wish to use NETPBM/PBMPLUS
# instead of the built-in "uncompressed" GIF encoder
# Image.register_save(GifImageFile.format, _save_netpbm)

View File

@ -0,0 +1,138 @@
#
# Python Imaging Library
# $Id$
#
# stuff to read (and render) GIMP gradient files
#
# History:
# 97-08-23 fl Created
#
# Copyright (c) Secret Labs AB 1997.
# Copyright (c) Fredrik Lundh 1997.
#
# See the README file for information on usage and redistribution.
#
from math import pi, log, sin, sqrt
from ._binary import o8
# --------------------------------------------------------------------
# Stuff to translate curve segments to palette values (derived from
# the corresponding code in GIMP, written by Federico Mena Quintero.
# See the GIMP distribution for more information.)
#
EPSILON = 1e-10
def linear(middle, pos):
if pos <= middle:
if middle < EPSILON:
return 0.0
else:
return 0.5 * pos / middle
else:
pos = pos - middle
middle = 1.0 - middle
if middle < EPSILON:
return 1.0
else:
return 0.5 + 0.5 * pos / middle
def curved(middle, pos):
return pos ** (log(0.5) / log(max(middle, EPSILON)))
def sine(middle, pos):
return (sin((-pi / 2.0) + pi * linear(middle, pos)) + 1.0) / 2.0
def sphere_increasing(middle, pos):
return sqrt(1.0 - (linear(middle, pos) - 1.0) ** 2)
def sphere_decreasing(middle, pos):
return 1.0 - sqrt(1.0 - linear(middle, pos) ** 2)
SEGMENTS = [linear, curved, sine, sphere_increasing, sphere_decreasing]
class GradientFile(object):
gradient = None
def getpalette(self, entries=256):
palette = []
ix = 0
x0, x1, xm, rgb0, rgb1, segment = self.gradient[ix]
for i in range(entries):
x = i / float(entries-1)
while x1 < x:
ix += 1
x0, x1, xm, rgb0, rgb1, segment = self.gradient[ix]
w = x1 - x0
if w < EPSILON:
scale = segment(0.5, 0.5)
else:
scale = segment((xm - x0) / w, (x - x0) / w)
# expand to RGBA
r = o8(int(255 * ((rgb1[0] - rgb0[0]) * scale + rgb0[0]) + 0.5))
g = o8(int(255 * ((rgb1[1] - rgb0[1]) * scale + rgb0[1]) + 0.5))
b = o8(int(255 * ((rgb1[2] - rgb0[2]) * scale + rgb0[2]) + 0.5))
a = o8(int(255 * ((rgb1[3] - rgb0[3]) * scale + rgb0[3]) + 0.5))
# add to palette
palette.append(r + g + b + a)
return b"".join(palette), "RGBA"
##
# File handler for GIMP's gradient format.
class GimpGradientFile(GradientFile):
def __init__(self, fp):
if fp.readline()[:13] != b"GIMP Gradient":
raise SyntaxError("not a GIMP gradient file")
line = fp.readline()
# GIMP 1.2 gradient files don't contain a name, but GIMP 1.3 files do
if line.startswith(b"Name: "):
line = fp.readline().strip()
count = int(line)
gradient = []
for i in range(count):
s = fp.readline().split()
w = [float(x) for x in s[:11]]
x0, x1 = w[0], w[2]
xm = w[1]
rgb0 = w[3:7]
rgb1 = w[7:11]
segment = SEGMENTS[int(s[11])]
cspace = int(s[12])
if cspace != 0:
raise IOError("cannot handle HSV colour space")
gradient.append((x0, x1, xm, rgb0, rgb1, segment))
self.gradient = gradient

View File

@ -0,0 +1,62 @@
#
# Python Imaging Library
# $Id$
#
# stuff to read GIMP palette files
#
# History:
# 1997-08-23 fl Created
# 2004-09-07 fl Support GIMP 2.0 palette files.
#
# Copyright (c) Secret Labs AB 1997-2004. All rights reserved.
# Copyright (c) Fredrik Lundh 1997-2004.
#
# See the README file for information on usage and redistribution.
#
import re
from ._binary import o8
##
# File handler for GIMP's palette format.
class GimpPaletteFile(object):
rawmode = "RGB"
def __init__(self, fp):
self.palette = [o8(i)*3 for i in range(256)]
if fp.readline()[:12] != b"GIMP Palette":
raise SyntaxError("not a GIMP palette file")
i = 0
while i <= 255:
s = fp.readline()
if not s:
break
# skip fields and comment lines
if re.match(br"\w+:|#", s):
continue
if len(s) > 100:
raise SyntaxError("bad palette file")
v = tuple(map(int, s.split()[:3]))
if len(v) != 3:
raise ValueError("bad palette entry")
if 0 <= i <= 255:
self.palette[i] = o8(v[0]) + o8(v[1]) + o8(v[2])
i += 1
self.palette = b"".join(self.palette)
def getpalette(self):
return self.palette, self.rawmode

View File

@ -0,0 +1,73 @@
#
# The Python Imaging Library
# $Id$
#
# GRIB stub adapter
#
# Copyright (c) 1996-2003 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
from . import Image, ImageFile
from ._binary import i8
_handler = None
def register_handler(handler):
"""
Install application-specific GRIB image handler.
:param handler: Handler object.
"""
global _handler
_handler = handler
# --------------------------------------------------------------------
# Image adapter
def _accept(prefix):
return prefix[0:4] == b"GRIB" and i8(prefix[7]) == 1
class GribStubImageFile(ImageFile.StubImageFile):
format = "GRIB"
format_description = "GRIB"
def _open(self):
offset = self.fp.tell()
if not _accept(self.fp.read(8)):
raise SyntaxError("Not a GRIB file")
self.fp.seek(offset)
# make something up
self.mode = "F"
self._size = 1, 1
loader = self._load()
if loader:
loader.open(self)
def _load(self):
return _handler
def _save(im, fp, filename):
if _handler is None or not hasattr("_handler", "save"):
raise IOError("GRIB save handler not installed")
_handler.save(im, fp, filename)
# --------------------------------------------------------------------
# Registry
Image.register_open(GribStubImageFile.format, GribStubImageFile, _accept)
Image.register_save(GribStubImageFile.format, _save)
Image.register_extension(GribStubImageFile.format, ".grib")

View File

@ -0,0 +1,72 @@
#
# The Python Imaging Library
# $Id$
#
# HDF5 stub adapter
#
# Copyright (c) 2000-2003 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
from . import Image, ImageFile
_handler = None
def register_handler(handler):
"""
Install application-specific HDF5 image handler.
:param handler: Handler object.
"""
global _handler
_handler = handler
# --------------------------------------------------------------------
# Image adapter
def _accept(prefix):
return prefix[:8] == b"\x89HDF\r\n\x1a\n"
class HDF5StubImageFile(ImageFile.StubImageFile):
format = "HDF5"
format_description = "HDF5"
def _open(self):
offset = self.fp.tell()
if not _accept(self.fp.read(8)):
raise SyntaxError("Not an HDF file")
self.fp.seek(offset)
# make something up
self.mode = "F"
self._size = 1, 1
loader = self._load()
if loader:
loader.open(self)
def _load(self):
return _handler
def _save(im, fp, filename):
if _handler is None or not hasattr("_handler", "save"):
raise IOError("HDF5 save handler not installed")
_handler.save(im, fp, filename)
# --------------------------------------------------------------------
# Registry
Image.register_open(HDF5StubImageFile.format, HDF5StubImageFile, _accept)
Image.register_save(HDF5StubImageFile.format, _save)
Image.register_extensions(HDF5StubImageFile.format, [".h5", ".hdf"])

View File

@ -0,0 +1,397 @@
#
# The Python Imaging Library.
# $Id$
#
# macOS icns file decoder, based on icns.py by Bob Ippolito.
#
# history:
# 2004-10-09 fl Turned into a PIL plugin; removed 2.3 dependencies.
#
# Copyright (c) 2004 by Bob Ippolito.
# Copyright (c) 2004 by Secret Labs.
# Copyright (c) 2004 by Fredrik Lundh.
# Copyright (c) 2014 by Alastair Houghton.
#
# See the README file for information on usage and redistribution.
#
from PIL import Image, ImageFile, PngImagePlugin
from PIL._binary import i8
import io
import os
import shutil
import struct
import sys
import tempfile
enable_jpeg2k = hasattr(Image.core, 'jp2klib_version')
if enable_jpeg2k:
from PIL import Jpeg2KImagePlugin
HEADERSIZE = 8
def nextheader(fobj):
return struct.unpack('>4sI', fobj.read(HEADERSIZE))
def read_32t(fobj, start_length, size):
# The 128x128 icon seems to have an extra header for some reason.
(start, length) = start_length
fobj.seek(start)
sig = fobj.read(4)
if sig != b'\x00\x00\x00\x00':
raise SyntaxError('Unknown signature, expecting 0x00000000')
return read_32(fobj, (start + 4, length - 4), size)
def read_32(fobj, start_length, size):
"""
Read a 32bit RGB icon resource. Seems to be either uncompressed or
an RLE packbits-like scheme.
"""
(start, length) = start_length
fobj.seek(start)
pixel_size = (size[0] * size[2], size[1] * size[2])
sizesq = pixel_size[0] * pixel_size[1]
if length == sizesq * 3:
# uncompressed ("RGBRGBGB")
indata = fobj.read(length)
im = Image.frombuffer("RGB", pixel_size, indata, "raw", "RGB", 0, 1)
else:
# decode image
im = Image.new("RGB", pixel_size, None)
for band_ix in range(3):
data = []
bytesleft = sizesq
while bytesleft > 0:
byte = fobj.read(1)
if not byte:
break
byte = i8(byte)
if byte & 0x80:
blocksize = byte - 125
byte = fobj.read(1)
for i in range(blocksize):
data.append(byte)
else:
blocksize = byte + 1
data.append(fobj.read(blocksize))
bytesleft -= blocksize
if bytesleft <= 0:
break
if bytesleft != 0:
raise SyntaxError(
"Error reading channel [%r left]" % bytesleft
)
band = Image.frombuffer(
"L", pixel_size, b"".join(data), "raw", "L", 0, 1
)
im.im.putband(band.im, band_ix)
return {"RGB": im}
def read_mk(fobj, start_length, size):
# Alpha masks seem to be uncompressed
start = start_length[0]
fobj.seek(start)
pixel_size = (size[0] * size[2], size[1] * size[2])
sizesq = pixel_size[0] * pixel_size[1]
band = Image.frombuffer(
"L", pixel_size, fobj.read(sizesq), "raw", "L", 0, 1
)
return {"A": band}
def read_png_or_jpeg2000(fobj, start_length, size):
(start, length) = start_length
fobj.seek(start)
sig = fobj.read(12)
if sig[:8] == b'\x89PNG\x0d\x0a\x1a\x0a':
fobj.seek(start)
im = PngImagePlugin.PngImageFile(fobj)
return {"RGBA": im}
elif sig[:4] == b'\xff\x4f\xff\x51' \
or sig[:4] == b'\x0d\x0a\x87\x0a' \
or sig == b'\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a':
if not enable_jpeg2k:
raise ValueError('Unsupported icon subimage format (rebuild PIL '
'with JPEG 2000 support to fix this)')
# j2k, jpc or j2c
fobj.seek(start)
jp2kstream = fobj.read(length)
f = io.BytesIO(jp2kstream)
im = Jpeg2KImagePlugin.Jpeg2KImageFile(f)
if im.mode != 'RGBA':
im = im.convert('RGBA')
return {"RGBA": im}
else:
raise ValueError('Unsupported icon subimage format')
class IcnsFile(object):
SIZES = {
(512, 512, 2): [
(b'ic10', read_png_or_jpeg2000),
],
(512, 512, 1): [
(b'ic09', read_png_or_jpeg2000),
],
(256, 256, 2): [
(b'ic14', read_png_or_jpeg2000),
],
(256, 256, 1): [
(b'ic08', read_png_or_jpeg2000),
],
(128, 128, 2): [
(b'ic13', read_png_or_jpeg2000),
],
(128, 128, 1): [
(b'ic07', read_png_or_jpeg2000),
(b'it32', read_32t),
(b't8mk', read_mk),
],
(64, 64, 1): [
(b'icp6', read_png_or_jpeg2000),
],
(32, 32, 2): [
(b'ic12', read_png_or_jpeg2000),
],
(48, 48, 1): [
(b'ih32', read_32),
(b'h8mk', read_mk),
],
(32, 32, 1): [
(b'icp5', read_png_or_jpeg2000),
(b'il32', read_32),
(b'l8mk', read_mk),
],
(16, 16, 2): [
(b'ic11', read_png_or_jpeg2000),
],
(16, 16, 1): [
(b'icp4', read_png_or_jpeg2000),
(b'is32', read_32),
(b's8mk', read_mk),
],
}
def __init__(self, fobj):
"""
fobj is a file-like object as an icns resource
"""
# signature : (start, length)
self.dct = dct = {}
self.fobj = fobj
sig, filesize = nextheader(fobj)
if sig != b'icns':
raise SyntaxError('not an icns file')
i = HEADERSIZE
while i < filesize:
sig, blocksize = nextheader(fobj)
if blocksize <= 0:
raise SyntaxError('invalid block header')
i += HEADERSIZE
blocksize -= HEADERSIZE
dct[sig] = (i, blocksize)
fobj.seek(blocksize, 1)
i += blocksize
def itersizes(self):
sizes = []
for size, fmts in self.SIZES.items():
for (fmt, reader) in fmts:
if fmt in self.dct:
sizes.append(size)
break
return sizes
def bestsize(self):
sizes = self.itersizes()
if not sizes:
raise SyntaxError("No 32bit icon resources found")
return max(sizes)
def dataforsize(self, size):
"""
Get an icon resource as {channel: array}. Note that
the arrays are bottom-up like windows bitmaps and will likely
need to be flipped or transposed in some way.
"""
dct = {}
for code, reader in self.SIZES[size]:
desc = self.dct.get(code)
if desc is not None:
dct.update(reader(self.fobj, desc, size))
return dct
def getimage(self, size=None):
if size is None:
size = self.bestsize()
if len(size) == 2:
size = (size[0], size[1], 1)
channels = self.dataforsize(size)
im = channels.get('RGBA', None)
if im:
return im
im = channels.get("RGB").copy()
try:
im.putalpha(channels["A"])
except KeyError:
pass
return im
##
# Image plugin for Mac OS icons.
class IcnsImageFile(ImageFile.ImageFile):
"""
PIL image support for Mac OS .icns files.
Chooses the best resolution, but will possibly load
a different size image if you mutate the size attribute
before calling 'load'.
The info dictionary has a key 'sizes' that is a list
of sizes that the icns file has.
"""
format = "ICNS"
format_description = "Mac OS icns resource"
def _open(self):
self.icns = IcnsFile(self.fp)
self.mode = 'RGBA'
self.info['sizes'] = self.icns.itersizes()
self.best_size = self.icns.bestsize()
self.size = (self.best_size[0] * self.best_size[2],
self.best_size[1] * self.best_size[2])
# Just use this to see if it's loaded or not yet.
self.tile = ('',)
@property
def size(self):
return self._size
@size.setter
def size(self, value):
info_size = value
if info_size not in self.info['sizes'] and len(info_size) == 2:
info_size = (info_size[0], info_size[1], 1)
if info_size not in self.info['sizes'] and len(info_size) == 3 and \
info_size[2] == 1:
simple_sizes = [(size[0] * size[2], size[1] * size[2])
for size in self.info['sizes']]
if value in simple_sizes:
info_size = self.info['sizes'][simple_sizes.index(value)]
if info_size not in self.info['sizes']:
raise ValueError(
"This is not one of the allowed sizes of this image")
self._size = value
def load(self):
if len(self.size) == 3:
self.best_size = self.size
self.size = (self.best_size[0] * self.best_size[2],
self.best_size[1] * self.best_size[2])
Image.Image.load(self)
if not self.tile:
return
self.load_prepare()
# This is likely NOT the best way to do it, but whatever.
im = self.icns.getimage(self.best_size)
# If this is a PNG or JPEG 2000, it won't be loaded yet
im.load()
self.im = im.im
self.mode = im.mode
self.size = im.size
if self._exclusive_fp:
self.fp.close()
self.fp = None
self.icns = None
self.tile = ()
self.load_end()
def _save(im, fp, filename):
"""
Saves the image as a series of PNG files,
that are then converted to a .icns file
using the macOS command line utility 'iconutil'.
macOS only.
"""
if hasattr(fp, "flush"):
fp.flush()
# create the temporary set of pngs
iconset = tempfile.mkdtemp('.iconset')
provided_images = {im.width: im
for im in im.encoderinfo.get("append_images", [])}
last_w = None
second_path = None
for w in [16, 32, 128, 256, 512]:
prefix = 'icon_{}x{}'.format(w, w)
first_path = os.path.join(iconset, prefix+'.png')
if last_w == w:
shutil.copyfile(second_path, first_path)
else:
im_w = provided_images.get(w, im.resize((w, w), Image.LANCZOS))
im_w.save(first_path)
second_path = os.path.join(iconset, prefix+'@2x.png')
im_w2 = provided_images.get(w*2, im.resize((w*2, w*2), Image.LANCZOS))
im_w2.save(second_path)
last_w = w*2
# iconutil -c icns -o {} {}
from subprocess import Popen, PIPE, CalledProcessError
convert_cmd = ["iconutil", "-c", "icns", "-o", filename, iconset]
with open(os.devnull, 'wb') as devnull:
convert_proc = Popen(convert_cmd, stdout=PIPE, stderr=devnull)
convert_proc.stdout.close()
retcode = convert_proc.wait()
# remove the temporary files
shutil.rmtree(iconset)
if retcode:
raise CalledProcessError(retcode, convert_cmd)
Image.register_open(IcnsImageFile.format, IcnsImageFile,
lambda x: x[:4] == b'icns')
Image.register_extension(IcnsImageFile.format, '.icns')
if sys.platform == 'darwin':
Image.register_save(IcnsImageFile.format, _save)
Image.register_mime(IcnsImageFile.format, "image/icns")
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Syntax: python IcnsImagePlugin.py [file]")
sys.exit()
imf = IcnsImageFile(open(sys.argv[1], 'rb'))
for size in imf.info['sizes']:
imf.size = size
imf.load()
im = imf.im
im.save('out-%s-%s-%s.png' % size)
im = Image.open(sys.argv[1])
im.save("out.png")
if sys.platform == 'windows':
os.startfile("out.png")

View File

@ -0,0 +1,295 @@
#
# The Python Imaging Library.
# $Id$
#
# Windows Icon support for PIL
#
# History:
# 96-05-27 fl Created
#
# Copyright (c) Secret Labs AB 1997.
# Copyright (c) Fredrik Lundh 1996.
#
# See the README file for information on usage and redistribution.
#
# This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis
# <casadebender@gmail.com>.
# https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki
#
# Icon format references:
# * https://en.wikipedia.org/wiki/ICO_(file_format)
# * https://msdn.microsoft.com/en-us/library/ms997538.aspx
import struct
from io import BytesIO
from . import Image, ImageFile, BmpImagePlugin, PngImagePlugin
from ._binary import i8, i16le as i16, i32le as i32
from math import log, ceil
__version__ = "0.1"
#
# --------------------------------------------------------------------
_MAGIC = b"\0\0\1\0"
def _save(im, fp, filename):
fp.write(_MAGIC) # (2+2)
sizes = im.encoderinfo.get("sizes",
[(16, 16), (24, 24), (32, 32), (48, 48),
(64, 64), (128, 128), (256, 256)])
width, height = im.size
sizes = filter(lambda x: False if (x[0] > width or x[1] > height or
x[0] > 256 or x[1] > 256) else True,
sizes)
sizes = list(sizes)
fp.write(struct.pack("<H", len(sizes))) # idCount(2)
offset = fp.tell() + len(sizes)*16
for size in sizes:
width, height = size
# 0 means 256
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") # bReserved(1)
fp.write(b"\0\0") # wPlanes(2)
fp.write(struct.pack("<H", 32)) # wBitCount(2)
image_io = BytesIO()
tmp = im.copy()
tmp.thumbnail(size, Image.LANCZOS)
tmp.save(image_io, "png")
image_io.seek(0)
image_bytes = image_io.read()
bytes_len = len(image_bytes)
fp.write(struct.pack("<I", bytes_len)) # dwBytesInRes(4)
fp.write(struct.pack("<I", offset)) # dwImageOffset(4)
current = fp.tell()
fp.seek(offset)
fp.write(image_bytes)
offset = offset + bytes_len
fp.seek(current)
def _accept(prefix):
return prefix[:4] == _MAGIC
class IcoFile(object):
def __init__(self, buf):
"""
Parse image from file-like object containing ico file data
"""
# check magic
s = buf.read(6)
if not _accept(s):
raise SyntaxError("not an ICO file")
self.buf = buf
self.entry = []
# Number of items in file
self.nb_items = i16(s[4:])
# Get headers for each item
for i in range(self.nb_items):
s = buf.read(16)
icon_header = {
'width': i8(s[0]),
'height': i8(s[1]),
'nb_color': i8(s[2]), # No. of colors in image (0 if >=8bpp)
'reserved': i8(s[3]),
'planes': i16(s[4:]),
'bpp': i16(s[6:]),
'size': i32(s[8:]),
'offset': i32(s[12:])
}
# See Wikipedia
for j in ('width', 'height'):
if not icon_header[j]:
icon_header[j] = 256
# See Wikipedia notes about color depth.
# We need this just to differ images with equal sizes
icon_header['color_depth'] = (icon_header['bpp'] or
(icon_header['nb_color'] != 0 and
ceil(log(icon_header['nb_color'],
2))) or 256)
icon_header['dim'] = (icon_header['width'], icon_header['height'])
icon_header['square'] = (icon_header['width'] *
icon_header['height'])
self.entry.append(icon_header)
self.entry = sorted(self.entry, key=lambda x: x['color_depth'])
# ICO images are usually squares
# self.entry = sorted(self.entry, key=lambda x: x['width'])
self.entry = sorted(self.entry, key=lambda x: x['square'])
self.entry.reverse()
def sizes(self):
"""
Get a list of all available icon sizes and color depths.
"""
return {(h['width'], h['height']) for h in self.entry}
def getimage(self, size, bpp=False):
"""
Get an image from the icon
"""
for (i, h) in enumerate(self.entry):
if size == h['dim'] and (bpp is False or bpp == h['color_depth']):
return self.frame(i)
return self.frame(0)
def frame(self, idx):
"""
Get an image from frame idx
"""
header = self.entry[idx]
self.buf.seek(header['offset'])
data = self.buf.read(8)
self.buf.seek(header['offset'])
if data[:8] == PngImagePlugin._MAGIC:
# png frame
im = PngImagePlugin.PngImageFile(self.buf)
else:
# XOR + AND mask bmp frame
im = BmpImagePlugin.DibImageFile(self.buf)
# change tile dimension to only encompass XOR image
im._size = (im.size[0], int(im.size[1] / 2))
d, e, o, a = im.tile[0]
im.tile[0] = d, (0, 0) + im.size, o, a
# figure out where AND mask image starts
mode = a[0]
bpp = 8
for k, v in BmpImagePlugin.BIT2MODE.items():
if mode == v[1]:
bpp = k
break
if 32 == bpp:
# 32-bit color depth icon image allows semitransparent areas
# PIL's DIB format ignores transparency bits, recover them.
# The DIB is packed in BGRX byte order where X is the alpha
# channel.
# Back up to start of bmp data
self.buf.seek(o)
# extract every 4th byte (eg. 3,7,11,15,...)
alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4]
# convert to an 8bpp grayscale image
mask = Image.frombuffer(
'L', # 8bpp
im.size, # (w, h)
alpha_bytes, # source chars
'raw', # raw decoder
('L', 0, -1) # 8bpp inverted, unpadded, reversed
)
else:
# get AND image from end of bitmap
w = im.size[0]
if (w % 32) > 0:
# bitmap row data is aligned to word boundaries
w += 32 - (im.size[0] % 32)
# the total mask data is
# padded row size * height / bits per char
and_mask_offset = o + int(im.size[0] * im.size[1] *
(bpp / 8.0))
total_bytes = int((w * im.size[1]) / 8)
self.buf.seek(and_mask_offset)
mask_data = self.buf.read(total_bytes)
# convert raw data to image
mask = Image.frombuffer(
'1', # 1 bpp
im.size, # (w, h)
mask_data, # source chars
'raw', # raw decoder
('1;I', int(w/8), -1) # 1bpp inverted, padded, reversed
)
# now we have two images, im is XOR image and mask is AND image
# apply mask image as alpha channel
im = im.convert('RGBA')
im.putalpha(mask)
return im
##
# Image plugin for Windows Icon files.
class IcoImageFile(ImageFile.ImageFile):
"""
PIL read-only image support for Microsoft Windows .ico files.
By default the largest resolution image in the file will be loaded. This
can be changed by altering the 'size' attribute before calling 'load'.
The info dictionary has a key 'sizes' that is a list of the sizes available
in the icon file.
Handles classic, XP and Vista icon formats.
This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis
<casadebender@gmail.com>.
https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki
"""
format = "ICO"
format_description = "Windows Icon"
def _open(self):
self.ico = IcoFile(self.fp)
self.info['sizes'] = self.ico.sizes()
self.size = self.ico.entry[0]['dim']
self.load()
@property
def size(self):
return self._size
@size.setter
def size(self, value):
if value not in self.info['sizes']:
raise ValueError(
"This is not one of the allowed sizes of this image")
self._size = value
def load(self):
im = self.ico.getimage(self.size)
# if tile is PNG, it won't really be loaded yet
im.load()
self.im = im.im
self.mode = im.mode
self.size = im.size
def load_seek(self):
# Flag the ImageFile.Parser so that it
# just does all the decode at the end.
pass
#
# --------------------------------------------------------------------
Image.register_open(IcoImageFile.format, IcoImageFile, _accept)
Image.register_save(IcoImageFile.format, _save)
Image.register_extension(IcoImageFile.format, ".ico")

View File

@ -0,0 +1,356 @@
#
# The Python Imaging Library.
# $Id$
#
# IFUNC IM file handling for PIL
#
# history:
# 1995-09-01 fl Created.
# 1997-01-03 fl Save palette images
# 1997-01-08 fl Added sequence support
# 1997-01-23 fl Added P and RGB save support
# 1997-05-31 fl Read floating point images
# 1997-06-22 fl Save floating point images
# 1997-08-27 fl Read and save 1-bit images
# 1998-06-25 fl Added support for RGB+LUT images
# 1998-07-02 fl Added support for YCC images
# 1998-07-15 fl Renamed offset attribute to avoid name clash
# 1998-12-29 fl Added I;16 support
# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.7)
# 2003-09-26 fl Added LA/PA support
#
# Copyright (c) 1997-2003 by Secret Labs AB.
# Copyright (c) 1995-2001 by Fredrik Lundh.
#
# See the README file for information on usage and redistribution.
#
import re
from . import Image, ImageFile, ImagePalette
from ._binary import i8
__version__ = "0.7"
# --------------------------------------------------------------------
# Standard tags
COMMENT = "Comment"
DATE = "Date"
EQUIPMENT = "Digitalization equipment"
FRAMES = "File size (no of images)"
LUT = "Lut"
NAME = "Name"
SCALE = "Scale (x,y)"
SIZE = "Image size (x*y)"
MODE = "Image type"
TAGS = {COMMENT: 0, DATE: 0, EQUIPMENT: 0, FRAMES: 0, LUT: 0, NAME: 0,
SCALE: 0, SIZE: 0, MODE: 0}
OPEN = {
# ifunc93/p3cfunc formats
"0 1 image": ("1", "1"),
"L 1 image": ("1", "1"),
"Greyscale image": ("L", "L"),
"Grayscale image": ("L", "L"),
"RGB image": ("RGB", "RGB;L"),
"RLB image": ("RGB", "RLB"),
"RYB image": ("RGB", "RLB"),
"B1 image": ("1", "1"),
"B2 image": ("P", "P;2"),
"B4 image": ("P", "P;4"),
"X 24 image": ("RGB", "RGB"),
"L 32 S image": ("I", "I;32"),
"L 32 F image": ("F", "F;32"),
# old p3cfunc formats
"RGB3 image": ("RGB", "RGB;T"),
"RYB3 image": ("RGB", "RYB;T"),
# extensions
"LA image": ("LA", "LA;L"),
"RGBA image": ("RGBA", "RGBA;L"),
"RGBX image": ("RGBX", "RGBX;L"),
"CMYK image": ("CMYK", "CMYK;L"),
"YCC image": ("YCbCr", "YCbCr;L"),
}
# ifunc95 extensions
for i in ["8", "8S", "16", "16S", "32", "32F"]:
OPEN["L %s image" % i] = ("F", "F;%s" % i)
OPEN["L*%s image" % i] = ("F", "F;%s" % i)
for i in ["16", "16L", "16B"]:
OPEN["L %s image" % i] = ("I;%s" % i, "I;%s" % i)
OPEN["L*%s image" % i] = ("I;%s" % i, "I;%s" % i)
for i in ["32S"]:
OPEN["L %s image" % i] = ("I", "I;%s" % i)
OPEN["L*%s image" % i] = ("I", "I;%s" % i)
for i in range(2, 33):
OPEN["L*%s image" % i] = ("F", "F;%s" % i)
# --------------------------------------------------------------------
# Read IM directory
split = re.compile(br"^([A-Za-z][^:]*):[ \t]*(.*)[ \t]*$")
def number(s):
try:
return int(s)
except ValueError:
return float(s)
##
# Image plugin for the IFUNC IM file format.
class ImImageFile(ImageFile.ImageFile):
format = "IM"
format_description = "IFUNC Image Memory"
_close_exclusive_fp_after_loading = False
def _open(self):
# Quick rejection: if there's not an LF among the first
# 100 bytes, this is (probably) not a text header.
if b"\n" not in self.fp.read(100):
raise SyntaxError("not an IM file")
self.fp.seek(0)
n = 0
# Default values
self.info[MODE] = "L"
self.info[SIZE] = (512, 512)
self.info[FRAMES] = 1
self.rawmode = "L"
while True:
s = self.fp.read(1)
# Some versions of IFUNC uses \n\r instead of \r\n...
if s == b"\r":
continue
if not s or s == b'\0' or s == b'\x1A':
break
# FIXME: this may read whole file if not a text file
s = s + self.fp.readline()
if len(s) > 100:
raise SyntaxError("not an IM file")
if s[-2:] == b'\r\n':
s = s[:-2]
elif s[-1:] == b'\n':
s = s[:-1]
try:
m = split.match(s)
except re.error:
raise SyntaxError("not an IM file")
if m:
k, v = m.group(1, 2)
# Don't know if this is the correct encoding,
# but a decent guess (I guess)
k = k.decode('latin-1', 'replace')
v = v.decode('latin-1', 'replace')
# Convert value as appropriate
if k in [FRAMES, SCALE, SIZE]:
v = v.replace("*", ",")
v = tuple(map(number, v.split(",")))
if len(v) == 1:
v = v[0]
elif k == MODE and v in OPEN:
v, self.rawmode = OPEN[v]
# Add to dictionary. Note that COMMENT tags are
# combined into a list of strings.
if k == COMMENT:
if k in self.info:
self.info[k].append(v)
else:
self.info[k] = [v]
else:
self.info[k] = v
if k in TAGS:
n += 1
else:
raise SyntaxError("Syntax error in IM header: " +
s.decode('ascii', 'replace'))
if not n:
raise SyntaxError("Not an IM file")
# Basic attributes
self._size = self.info[SIZE]
self.mode = self.info[MODE]
# Skip forward to start of image data
while s and s[0:1] != b'\x1A':
s = self.fp.read(1)
if not s:
raise SyntaxError("File truncated")
if LUT in self.info:
# convert lookup table to palette or lut attribute
palette = self.fp.read(768)
greyscale = 1 # greyscale palette
linear = 1 # linear greyscale palette
for i in range(256):
if palette[i] == palette[i+256] == palette[i+512]:
if i8(palette[i]) != i:
linear = 0
else:
greyscale = 0
if self.mode == "L" or self.mode == "LA":
if greyscale:
if not linear:
self.lut = [i8(c) for c in palette[:256]]
else:
if self.mode == "L":
self.mode = self.rawmode = "P"
elif self.mode == "LA":
self.mode = self.rawmode = "PA"
self.palette = ImagePalette.raw("RGB;L", palette)
elif self.mode == "RGB":
if not greyscale or not linear:
self.lut = [i8(c) for c in palette]
self.frame = 0
self.__offset = offs = self.fp.tell()
self.__fp = self.fp # FIXME: hack
if self.rawmode[:2] == "F;":
# ifunc95 formats
try:
# use bit decoder (if necessary)
bits = int(self.rawmode[2:])
if bits not in [8, 16, 32]:
self.tile = [("bit", (0, 0)+self.size, offs,
(bits, 8, 3, 0, -1))]
return
except ValueError:
pass
if self.rawmode in ["RGB;T", "RYB;T"]:
# Old LabEye/3PC files. Would be very surprised if anyone
# ever stumbled upon such a file ;-)
size = self.size[0] * self.size[1]
self.tile = [("raw", (0, 0)+self.size, offs, ("G", 0, -1)),
("raw", (0, 0)+self.size, offs+size, ("R", 0, -1)),
("raw", (0, 0)+self.size, offs+2*size, ("B", 0, -1))]
else:
# LabEye/IFUNC files
self.tile = [("raw", (0, 0)+self.size, offs,
(self.rawmode, 0, -1))]
@property
def n_frames(self):
return self.info[FRAMES]
@property
def is_animated(self):
return self.info[FRAMES] > 1
def seek(self, frame):
if not self._seek_check(frame):
return
self.frame = frame
if self.mode == "1":
bits = 1
else:
bits = 8 * len(self.mode)
size = ((self.size[0] * bits + 7) // 8) * self.size[1]
offs = self.__offset + frame * size
self.fp = self.__fp
self.tile = [("raw", (0, 0)+self.size, offs, (self.rawmode, 0, -1))]
def tell(self):
return self.frame
def _close__fp(self):
try:
if self.__fp != self.fp:
self.__fp.close()
except AttributeError:
pass
finally:
self.__fp = None
#
# --------------------------------------------------------------------
# Save IM files
SAVE = {
# mode: (im type, raw mode)
"1": ("0 1", "1"),
"L": ("Greyscale", "L"),
"LA": ("LA", "LA;L"),
"P": ("Greyscale", "P"),
"PA": ("LA", "PA;L"),
"I": ("L 32S", "I;32S"),
"I;16": ("L 16", "I;16"),
"I;16L": ("L 16L", "I;16L"),
"I;16B": ("L 16B", "I;16B"),
"F": ("L 32F", "F;32F"),
"RGB": ("RGB", "RGB;L"),
"RGBA": ("RGBA", "RGBA;L"),
"RGBX": ("RGBX", "RGBX;L"),
"CMYK": ("CMYK", "CMYK;L"),
"YCbCr": ("YCC", "YCbCr;L")
}
def _save(im, fp, filename):
try:
image_type, rawmode = SAVE[im.mode]
except KeyError:
raise ValueError("Cannot save %s images as IM" % im.mode)
frames = im.encoderinfo.get("frames", 1)
fp.write(("Image type: %s image\r\n" % image_type).encode('ascii'))
if filename:
fp.write(("Name: %s\r\n" % filename).encode('ascii'))
fp.write(("Image size (x*y): %d*%d\r\n" % im.size).encode('ascii'))
fp.write(("File size (no of images): %d\r\n" % frames).encode('ascii'))
if im.mode == "P":
fp.write(b"Lut: 1\r\n")
fp.write(b"\000" * (511-fp.tell()) + b"\032")
if im.mode == "P":
fp.write(im.im.getpalette("RGB", "RGB;L")) # 768 bytes
ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, -1))])
#
# --------------------------------------------------------------------
# Registry
Image.register_open(ImImageFile.format, ImImageFile)
Image.register_save(ImImageFile.format, _save)
Image.register_extension(ImImageFile.format, ".im")

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,292 @@
#
# The Python Imaging Library.
# $Id$
#
# standard channel operations
#
# History:
# 1996-03-24 fl Created
# 1996-08-13 fl Added logical operations (for "1" images)
# 2000-10-12 fl Added offset method (from Image.py)
#
# Copyright (c) 1997-2000 by Secret Labs AB
# Copyright (c) 1996-2000 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
from . import Image
def constant(image, value):
"""Fill a channel with a given grey level.
:rtype: :py:class:`~PIL.Image.Image`
"""
return Image.new("L", image.size, value)
def duplicate(image):
"""Copy a channel. Alias for :py:meth:`PIL.Image.Image.copy`.
:rtype: :py:class:`~PIL.Image.Image`
"""
return image.copy()
def invert(image):
"""
Invert an image (channel).
.. code-block:: python
out = MAX - image
:rtype: :py:class:`~PIL.Image.Image`
"""
image.load()
return image._new(image.im.chop_invert())
def lighter(image1, image2):
"""
Compares the two images, pixel by pixel, and returns a new image containing
the lighter values. At least one of the images must have mode "1".
.. code-block:: python
out = max(image1, image2)
:rtype: :py:class:`~PIL.Image.Image`
"""
image1.load()
image2.load()
return image1._new(image1.im.chop_lighter(image2.im))
def darker(image1, image2):
"""
Compares the two images, pixel by pixel, and returns a new image containing
the darker values. At least one of the images must have mode "1".
.. code-block:: python
out = min(image1, image2)
:rtype: :py:class:`~PIL.Image.Image`
"""
image1.load()
image2.load()
return image1._new(image1.im.chop_darker(image2.im))
def difference(image1, image2):
"""
Returns the absolute value of the pixel-by-pixel difference between the two
images. At least one of the images must have mode "1".
.. code-block:: python
out = abs(image1 - image2)
:rtype: :py:class:`~PIL.Image.Image`
"""
image1.load()
image2.load()
return image1._new(image1.im.chop_difference(image2.im))
def multiply(image1, image2):
"""
Superimposes two images on top of each other.
If you multiply an image with a solid black image, the result is black. If
you multiply with a solid white image, the image is unaffected. At least
one of the images must have mode "1".
.. code-block:: python
out = image1 * image2 / MAX
:rtype: :py:class:`~PIL.Image.Image`
"""
image1.load()
image2.load()
return image1._new(image1.im.chop_multiply(image2.im))
def screen(image1, image2):
"""
Superimposes two inverted images on top of each other. At least one of the
images must have mode "1".
.. code-block:: python
out = MAX - ((MAX - image1) * (MAX - image2) / MAX)
:rtype: :py:class:`~PIL.Image.Image`
"""
image1.load()
image2.load()
return image1._new(image1.im.chop_screen(image2.im))
def add(image1, image2, scale=1.0, offset=0):
"""
Adds two images, dividing the result by scale and adding the
offset. If omitted, scale defaults to 1.0, and offset to 0.0.
At least one of the images must have mode "1".
.. code-block:: python
out = ((image1 + image2) / scale + offset)
:rtype: :py:class:`~PIL.Image.Image`
"""
image1.load()
image2.load()
return image1._new(image1.im.chop_add(image2.im, scale, offset))
def subtract(image1, image2, scale=1.0, offset=0):
"""
Subtracts two images, dividing the result by scale and adding the offset.
If omitted, scale defaults to 1.0, and offset to 0.0. At least one of the
images must have mode "1".
.. code-block:: python
out = ((image1 - image2) / scale + offset)
:rtype: :py:class:`~PIL.Image.Image`
"""
image1.load()
image2.load()
return image1._new(image1.im.chop_subtract(image2.im, scale, offset))
def add_modulo(image1, image2):
"""Add two images, without clipping the result. At least one of the images
must have mode "1".
.. code-block:: python
out = ((image1 + image2) % MAX)
:rtype: :py:class:`~PIL.Image.Image`
"""
image1.load()
image2.load()
return image1._new(image1.im.chop_add_modulo(image2.im))
def subtract_modulo(image1, image2):
"""Subtract two images, without clipping the result. At least one of the
images must have mode "1".
.. code-block:: python
out = ((image1 - image2) % MAX)
:rtype: :py:class:`~PIL.Image.Image`
"""
image1.load()
image2.load()
return image1._new(image1.im.chop_subtract_modulo(image2.im))
def logical_and(image1, image2):
"""Logical AND between two images. At least one of the images must have
mode "1".
.. code-block:: python
out = ((image1 and image2) % MAX)
:rtype: :py:class:`~PIL.Image.Image`
"""
image1.load()
image2.load()
return image1._new(image1.im.chop_and(image2.im))
def logical_or(image1, image2):
"""Logical OR between two images. At least one of the images must have
mode "1".
.. code-block:: python
out = ((image1 or image2) % MAX)
:rtype: :py:class:`~PIL.Image.Image`
"""
image1.load()
image2.load()
return image1._new(image1.im.chop_or(image2.im))
def logical_xor(image1, image2):
"""Logical XOR between two images. At least one of the images must have
mode "1".
.. code-block:: python
out = ((bool(image1) != bool(image2)) % MAX)
:rtype: :py:class:`~PIL.Image.Image`
"""
image1.load()
image2.load()
return image1._new(image1.im.chop_xor(image2.im))
def blend(image1, image2, alpha):
"""Blend images using constant transparency weight. Alias for
:py:meth:`PIL.Image.Image.blend`.
:rtype: :py:class:`~PIL.Image.Image`
"""
return Image.blend(image1, image2, alpha)
def composite(image1, image2, mask):
"""Create composite using transparency mask. Alias for
:py:meth:`PIL.Image.Image.composite`.
:rtype: :py:class:`~PIL.Image.Image`
"""
return Image.composite(image1, image2, mask)
def offset(image, xoffset, yoffset=None):
"""Returns a copy of the image where data has been offset by the given
distances. Data wraps around the edges. If **yoffset** is omitted, it
is assumed to be equal to **xoffset**.
:param xoffset: The horizontal distance.
:param yoffset: The vertical distance. If omitted, both
distances are set to the same value.
:rtype: :py:class:`~PIL.Image.Image`
"""
if yoffset is None:
yoffset = xoffset
image.load()
return image._new(image.im.offset(xoffset, yoffset))

View File

@ -0,0 +1,955 @@
# The Python Imaging Library.
# $Id$
# Optional color management support, based on Kevin Cazabon's PyCMS
# library.
# History:
# 2009-03-08 fl Added to PIL.
# Copyright (C) 2002-2003 Kevin Cazabon
# Copyright (c) 2009 by Fredrik Lundh
# Copyright (c) 2013 by Eric Soroos
# See the README file for information on usage and redistribution. See
# below for the original description.
from __future__ import print_function
import sys
from PIL import Image
try:
from PIL import _imagingcms
except ImportError as ex:
# Allow error import for doc purposes, but error out when accessing
# anything in core.
from _util import deferred_error
_imagingcms = deferred_error(ex)
from PIL._util import isStringType
DESCRIPTION = """
pyCMS
a Python / PIL interface to the littleCMS ICC Color Management System
Copyright (C) 2002-2003 Kevin Cazabon
kevin@cazabon.com
http://www.cazabon.com
pyCMS home page: http://www.cazabon.com/pyCMS
littleCMS home page: http://www.littlecms.com
(littleCMS is Copyright (C) 1998-2001 Marti Maria)
Originally released under LGPL. Graciously donated to PIL in
March 2009, for distribution under the standard PIL license
The pyCMS.py module provides a "clean" interface between Python/PIL and
pyCMSdll, taking care of some of the more complex handling of the direct
pyCMSdll functions, as well as error-checking and making sure that all
relevant data is kept together.
While it is possible to call pyCMSdll functions directly, it's not highly
recommended.
Version History:
1.0.0 pil Oct 2013 Port to LCMS 2.
0.1.0 pil mod March 10, 2009
Renamed display profile to proof profile. The proof
profile is the profile of the device that is being
simulated, not the profile of the device which is
actually used to display/print the final simulation
(that'd be the output profile) - also see LCMSAPI.txt
input colorspace -> using 'renderingIntent' -> proof
colorspace -> using 'proofRenderingIntent' -> output
colorspace
Added LCMS FLAGS support.
Added FLAGS["SOFTPROOFING"] as default flag for
buildProofTransform (otherwise the proof profile/intent
would be ignored).
0.1.0 pil March 2009 - added to PIL, as PIL.ImageCms
0.0.2 alpha Jan 6, 2002
Added try/except statements around type() checks of
potential CObjects... Python won't let you use type()
on them, and raises a TypeError (stupid, if you ask
me!)
Added buildProofTransformFromOpenProfiles() function.
Additional fixes in DLL, see DLL code for details.
0.0.1 alpha first public release, Dec. 26, 2002
Known to-do list with current version (of Python interface, not pyCMSdll):
none
"""
VERSION = "1.0.0 pil"
# --------------------------------------------------------------------.
core = _imagingcms
#
# intent/direction values
INTENT_PERCEPTUAL = 0
INTENT_RELATIVE_COLORIMETRIC = 1
INTENT_SATURATION = 2
INTENT_ABSOLUTE_COLORIMETRIC = 3
DIRECTION_INPUT = 0
DIRECTION_OUTPUT = 1
DIRECTION_PROOF = 2
#
# flags
FLAGS = {
"MATRIXINPUT": 1,
"MATRIXOUTPUT": 2,
"MATRIXONLY": (1 | 2),
"NOWHITEONWHITEFIXUP": 4, # Don't hot fix scum dot
# Don't create prelinearization tables on precalculated transforms
# (internal use):
"NOPRELINEARIZATION": 16,
"GUESSDEVICECLASS": 32, # Guess device class (for transform2devicelink)
"NOTCACHE": 64, # Inhibit 1-pixel cache
"NOTPRECALC": 256,
"NULLTRANSFORM": 512, # Don't transform anyway
"HIGHRESPRECALC": 1024, # Use more memory to give better accuracy
"LOWRESPRECALC": 2048, # Use less memory to minimize resources
"WHITEBLACKCOMPENSATION": 8192,
"BLACKPOINTCOMPENSATION": 8192,
"GAMUTCHECK": 4096, # Out of Gamut alarm
"SOFTPROOFING": 16384, # Do softproofing
"PRESERVEBLACK": 32768, # Black preservation
"NODEFAULTRESOURCEDEF": 16777216, # CRD special
"GRIDPOINTS": lambda n: ((n) & 0xFF) << 16 # Gridpoints
}
_MAX_FLAG = 0
for flag in FLAGS.values():
if isinstance(flag, int):
_MAX_FLAG = _MAX_FLAG | flag
# --------------------------------------------------------------------.
# Experimental PIL-level API
# --------------------------------------------------------------------.
##
# Profile.
class ImageCmsProfile(object):
def __init__(self, profile):
"""
:param profile: Either a string representing a filename,
a file like object containing a profile or a
low-level profile object
"""
if isStringType(profile):
self._set(core.profile_open(profile), profile)
elif hasattr(profile, "read"):
self._set(core.profile_frombytes(profile.read()))
elif isinstance(profile, _imagingcms.CmsProfile):
self._set(profile)
else:
raise TypeError("Invalid type for Profile")
def _set(self, profile, filename=None):
self.profile = profile
self.filename = filename
if profile:
self.product_name = None # profile.product_name
self.product_info = None # profile.product_info
else:
self.product_name = None
self.product_info = None
def tobytes(self):
"""
Returns the profile in a format suitable for embedding in
saved images.
:returns: a bytes object containing the ICC profile.
"""
return core.profile_tobytes(self.profile)
class ImageCmsTransform(Image.ImagePointHandler):
"""
Transform. This can be used with the procedural API, or with the standard
Image.point() method.
Will return the output profile in the output.info['icc_profile'].
"""
def __init__(self, input, output, input_mode, output_mode,
intent=INTENT_PERCEPTUAL, proof=None,
proof_intent=INTENT_ABSOLUTE_COLORIMETRIC, flags=0):
if proof is None:
self.transform = core.buildTransform(
input.profile, output.profile,
input_mode, output_mode,
intent,
flags
)
else:
self.transform = core.buildProofTransform(
input.profile, output.profile, proof.profile,
input_mode, output_mode,
intent, proof_intent,
flags
)
# Note: inputMode and outputMode are for pyCMS compatibility only
self.input_mode = self.inputMode = input_mode
self.output_mode = self.outputMode = output_mode
self.output_profile = output
def point(self, im):
return self.apply(im)
def apply(self, im, imOut=None):
im.load()
if imOut is None:
imOut = Image.new(self.output_mode, im.size, None)
self.transform.apply(im.im.id, imOut.im.id)
imOut.info['icc_profile'] = self.output_profile.tobytes()
return imOut
def apply_in_place(self, im):
im.load()
if im.mode != self.output_mode:
raise ValueError("mode mismatch") # wrong output mode
self.transform.apply(im.im.id, im.im.id)
im.info['icc_profile'] = self.output_profile.tobytes()
return im
def get_display_profile(handle=None):
""" (experimental) Fetches the profile for the current display device.
:returns: None if the profile is not known.
"""
if sys.platform == "win32":
from PIL import ImageWin
if isinstance(handle, ImageWin.HDC):
profile = core.get_display_profile_win32(handle, 1)
else:
profile = core.get_display_profile_win32(handle or 0)
else:
try:
get = _imagingcms.get_display_profile
except AttributeError:
return None
else:
profile = get()
return ImageCmsProfile(profile)
# --------------------------------------------------------------------.
# pyCMS compatible layer
# --------------------------------------------------------------------.
class PyCMSError(Exception):
""" (pyCMS) Exception class.
This is used for all errors in the pyCMS API. """
pass
def profileToProfile(
im, inputProfile, outputProfile, renderingIntent=INTENT_PERCEPTUAL,
outputMode=None, inPlace=0, flags=0):
"""
(pyCMS) Applies an ICC transformation to a given image, mapping from
inputProfile to outputProfile.
If the input or output profiles specified are not valid filenames, a
PyCMSError will be raised. If inPlace == TRUE and outputMode != im.mode,
a PyCMSError will be raised. If an error occurs during application of
the profiles, a PyCMSError will be raised. If outputMode is not a mode
supported by the outputProfile (or by pyCMS), a PyCMSError will be
raised.
This function applies an ICC transformation to im from inputProfile's
color space to outputProfile's color space using the specified rendering
intent to decide how to handle out-of-gamut colors.
OutputMode can be used to specify that a color mode conversion is to
be done using these profiles, but the specified profiles must be able
to handle that mode. I.e., if converting im from RGB to CMYK using
profiles, the input profile must handle RGB data, and the output
profile must handle CMYK data.
:param im: An open PIL image object (i.e. Image.new(...) or
Image.open(...), etc.)
:param inputProfile: String, as a valid filename path to the ICC input
profile you wish to use for this image, or a profile object
:param outputProfile: String, as a valid filename path to the ICC output
profile you wish to use for this image, or a profile object
:param renderingIntent: Integer (0-3) specifying the rendering intent you
wish to use for the transform
ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT)
ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1
ImageCms.INTENT_SATURATION = 2
ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3
see the pyCMS documentation for details on rendering intents and what
they do.
:param outputMode: A valid PIL mode for the output image (i.e. "RGB",
"CMYK", etc.). Note: if rendering the image "inPlace", outputMode
MUST be the same mode as the input, or omitted completely. If
omitted, the outputMode will be the same as the mode of the input
image (im.mode)
:param inPlace: Boolean (1 = True, None or 0 = False). If True, the
original image is modified in-place, and None is returned. If False
(default), a new Image object is returned with the transform applied.
:param flags: Integer (0-...) specifying additional flags
:returns: Either None or a new PIL image object, depending on value of
inPlace
:exception PyCMSError:
"""
if outputMode is None:
outputMode = im.mode
if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3):
raise PyCMSError("renderingIntent must be an integer between 0 and 3")
if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
raise PyCMSError(
"flags must be an integer between 0 and %s" + _MAX_FLAG)
try:
if not isinstance(inputProfile, ImageCmsProfile):
inputProfile = ImageCmsProfile(inputProfile)
if not isinstance(outputProfile, ImageCmsProfile):
outputProfile = ImageCmsProfile(outputProfile)
transform = ImageCmsTransform(
inputProfile, outputProfile, im.mode, outputMode,
renderingIntent, flags=flags
)
if inPlace:
transform.apply_in_place(im)
imOut = None
else:
imOut = transform.apply(im)
except (IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
return imOut
def getOpenProfile(profileFilename):
"""
(pyCMS) Opens an ICC profile file.
The PyCMSProfile object can be passed back into pyCMS for use in creating
transforms and such (as in ImageCms.buildTransformFromOpenProfiles()).
If profileFilename is not a valid filename for an ICC profile, a PyCMSError
will be raised.
:param profileFilename: String, as a valid filename path to the ICC profile
you wish to open, or a file-like object.
:returns: A CmsProfile class object.
:exception PyCMSError:
"""
try:
return ImageCmsProfile(profileFilename)
except (IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
def buildTransform(
inputProfile, outputProfile, inMode, outMode,
renderingIntent=INTENT_PERCEPTUAL, flags=0):
"""
(pyCMS) Builds an ICC transform mapping from the inputProfile to the
outputProfile. Use applyTransform to apply the transform to a given
image.
If the input or output profiles specified are not valid filenames, a
PyCMSError will be raised. If an error occurs during creation of the
transform, a PyCMSError will be raised.
If inMode or outMode are not a mode supported by the outputProfile (or
by pyCMS), a PyCMSError will be raised.
This function builds and returns an ICC transform from the inputProfile
to the outputProfile using the renderingIntent to determine what to do
with out-of-gamut colors. It will ONLY work for converting images that
are in inMode to images that are in outMode color format (PIL mode,
i.e. "RGB", "RGBA", "CMYK", etc.).
Building the transform is a fair part of the overhead in
ImageCms.profileToProfile(), so if you're planning on converting multiple
images using the same input/output settings, this can save you time.
Once you have a transform object, it can be used with
ImageCms.applyProfile() to convert images without the need to re-compute
the lookup table for the transform.
The reason pyCMS returns a class object rather than a handle directly
to the transform is that it needs to keep track of the PIL input/output
modes that the transform is meant for. These attributes are stored in
the "inMode" and "outMode" attributes of the object (which can be
manually overridden if you really want to, but I don't know of any
time that would be of use, or would even work).
:param inputProfile: String, as a valid filename path to the ICC input
profile you wish to use for this transform, or a profile object
:param outputProfile: String, as a valid filename path to the ICC output
profile you wish to use for this transform, or a profile object
:param inMode: String, as a valid PIL mode that the appropriate profile
also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
:param outMode: String, as a valid PIL mode that the appropriate profile
also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
:param renderingIntent: Integer (0-3) specifying the rendering intent you
wish to use for the transform
ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT)
ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1
ImageCms.INTENT_SATURATION = 2
ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3
see the pyCMS documentation for details on rendering intents and what
they do.
:param flags: Integer (0-...) specifying additional flags
:returns: A CmsTransform class object.
:exception PyCMSError:
"""
if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3):
raise PyCMSError("renderingIntent must be an integer between 0 and 3")
if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
raise PyCMSError(
"flags must be an integer between 0 and %s" + _MAX_FLAG)
try:
if not isinstance(inputProfile, ImageCmsProfile):
inputProfile = ImageCmsProfile(inputProfile)
if not isinstance(outputProfile, ImageCmsProfile):
outputProfile = ImageCmsProfile(outputProfile)
return ImageCmsTransform(
inputProfile, outputProfile, inMode, outMode,
renderingIntent, flags=flags)
except (IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
def buildProofTransform(
inputProfile, outputProfile, proofProfile, inMode, outMode,
renderingIntent=INTENT_PERCEPTUAL,
proofRenderingIntent=INTENT_ABSOLUTE_COLORIMETRIC,
flags=FLAGS["SOFTPROOFING"]):
"""
(pyCMS) Builds an ICC transform mapping from the inputProfile to the
outputProfile, but tries to simulate the result that would be
obtained on the proofProfile device.
If the input, output, or proof profiles specified are not valid
filenames, a PyCMSError will be raised.
If an error occurs during creation of the transform, a PyCMSError will
be raised.
If inMode or outMode are not a mode supported by the outputProfile
(or by pyCMS), a PyCMSError will be raised.
This function builds and returns an ICC transform from the inputProfile
to the outputProfile, but tries to simulate the result that would be
obtained on the proofProfile device using renderingIntent and
proofRenderingIntent to determine what to do with out-of-gamut
colors. This is known as "soft-proofing". It will ONLY work for
converting images that are in inMode to images that are in outMode
color format (PIL mode, i.e. "RGB", "RGBA", "CMYK", etc.).
Usage of the resulting transform object is exactly the same as with
ImageCms.buildTransform().
Proof profiling is generally used when using an output device to get a
good idea of what the final printed/displayed image would look like on
the proofProfile device when it's quicker and easier to use the
output device for judging color. Generally, this means that the
output device is a monitor, or a dye-sub printer (etc.), and the simulated
device is something more expensive, complicated, or time consuming
(making it difficult to make a real print for color judgement purposes).
Soft-proofing basically functions by adjusting the colors on the
output device to match the colors of the device being simulated. However,
when the simulated device has a much wider gamut than the output
device, you may obtain marginal results.
:param inputProfile: String, as a valid filename path to the ICC input
profile you wish to use for this transform, or a profile object
:param outputProfile: String, as a valid filename path to the ICC output
(monitor, usually) profile you wish to use for this transform, or a
profile object
:param proofProfile: String, as a valid filename path to the ICC proof
profile you wish to use for this transform, or a profile object
:param inMode: String, as a valid PIL mode that the appropriate profile
also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
:param outMode: String, as a valid PIL mode that the appropriate profile
also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
:param renderingIntent: Integer (0-3) specifying the rendering intent you
wish to use for the input->proof (simulated) transform
ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT)
ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1
ImageCms.INTENT_SATURATION = 2
ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3
see the pyCMS documentation for details on rendering intents and what
they do.
:param proofRenderingIntent: Integer (0-3) specifying the rendering intent
you wish to use for proof->output transform
ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT)
ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1
ImageCms.INTENT_SATURATION = 2
ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3
see the pyCMS documentation for details on rendering intents and what
they do.
:param flags: Integer (0-...) specifying additional flags
:returns: A CmsTransform class object.
:exception PyCMSError:
"""
if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3):
raise PyCMSError("renderingIntent must be an integer between 0 and 3")
if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
raise PyCMSError(
"flags must be an integer between 0 and %s" + _MAX_FLAG)
try:
if not isinstance(inputProfile, ImageCmsProfile):
inputProfile = ImageCmsProfile(inputProfile)
if not isinstance(outputProfile, ImageCmsProfile):
outputProfile = ImageCmsProfile(outputProfile)
if not isinstance(proofProfile, ImageCmsProfile):
proofProfile = ImageCmsProfile(proofProfile)
return ImageCmsTransform(
inputProfile, outputProfile, inMode, outMode, renderingIntent,
proofProfile, proofRenderingIntent, flags)
except (IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
buildTransformFromOpenProfiles = buildTransform
buildProofTransformFromOpenProfiles = buildProofTransform
def applyTransform(im, transform, inPlace=0):
"""
(pyCMS) Applies a transform to a given image.
If im.mode != transform.inMode, a PyCMSError is raised.
If inPlace == TRUE and transform.inMode != transform.outMode, a
PyCMSError is raised.
If im.mode, transfer.inMode, or transfer.outMode is not supported by
pyCMSdll or the profiles you used for the transform, a PyCMSError is
raised.
If an error occurs while the transform is being applied, a PyCMSError
is raised.
This function applies a pre-calculated transform (from
ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles())
to an image. The transform can be used for multiple images, saving
considerable calculation time if doing the same conversion multiple times.
If you want to modify im in-place instead of receiving a new image as
the return value, set inPlace to TRUE. This can only be done if
transform.inMode and transform.outMode are the same, because we can't
change the mode in-place (the buffer sizes for some modes are
different). The default behavior is to return a new Image object of
the same dimensions in mode transform.outMode.
:param im: A PIL Image object, and im.mode must be the same as the inMode
supported by the transform.
:param transform: A valid CmsTransform class object
:param inPlace: Bool (1 == True, 0 or None == False). If True, im is
modified in place and None is returned, if False, a new Image object
with the transform applied is returned (and im is not changed). The
default is False.
:returns: Either None, or a new PIL Image object, depending on the value of
inPlace. The profile will be returned in the image's
info['icc_profile'].
:exception PyCMSError:
"""
try:
if inPlace:
transform.apply_in_place(im)
imOut = None
else:
imOut = transform.apply(im)
except (TypeError, ValueError) as v:
raise PyCMSError(v)
return imOut
def createProfile(colorSpace, colorTemp=-1):
"""
(pyCMS) Creates a profile.
If colorSpace not in ["LAB", "XYZ", "sRGB"], a PyCMSError is raised
If using LAB and colorTemp != a positive integer, a PyCMSError is raised.
If an error occurs while creating the profile, a PyCMSError is raised.
Use this function to create common profiles on-the-fly instead of
having to supply a profile on disk and knowing the path to it. It
returns a normal CmsProfile object that can be passed to
ImageCms.buildTransformFromOpenProfiles() to create a transform to apply
to images.
:param colorSpace: String, the color space of the profile you wish to
create.
Currently only "LAB", "XYZ", and "sRGB" are supported.
:param colorTemp: Positive integer for the white point for the profile, in
degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for D50
illuminant if omitted (5000k). colorTemp is ONLY applied to LAB
profiles, and is ignored for XYZ and sRGB.
:returns: A CmsProfile class object
:exception PyCMSError:
"""
if colorSpace not in ["LAB", "XYZ", "sRGB"]:
raise PyCMSError(
"Color space not supported for on-the-fly profile creation (%s)"
% colorSpace)
if colorSpace == "LAB":
try:
colorTemp = float(colorTemp)
except (TypeError, ValueError):
raise PyCMSError(
"Color temperature must be numeric, \"%s\" not valid"
% colorTemp)
try:
return core.createProfile(colorSpace, colorTemp)
except (TypeError, ValueError) as v:
raise PyCMSError(v)
def getProfileName(profile):
"""
(pyCMS) Gets the internal product name for the given profile.
If profile isn't a valid CmsProfile object or filename to a profile,
a PyCMSError is raised If an error occurs while trying to obtain the
name tag, a PyCMSError is raised.
Use this function to obtain the INTERNAL name of the profile (stored
in an ICC tag in the profile itself), usually the one used when the
profile was originally created. Sometimes this tag also contains
additional information supplied by the creator.
:param profile: EITHER a valid CmsProfile object, OR a string of the
filename of an ICC profile.
:returns: A string containing the internal name of the profile as stored
in an ICC tag.
:exception PyCMSError:
"""
try:
# add an extra newline to preserve pyCMS compatibility
if not isinstance(profile, ImageCmsProfile):
profile = ImageCmsProfile(profile)
# do it in python, not c.
# // name was "%s - %s" (model, manufacturer) || Description ,
# // but if the Model and Manufacturer were the same or the model
# // was long, Just the model, in 1.x
model = profile.profile.product_model
manufacturer = profile.profile.product_manufacturer
if not (model or manufacturer):
return profile.profile.product_description + "\n"
if not manufacturer or len(model) > 30:
return model + "\n"
return "%s - %s\n" % (model, manufacturer)
except (AttributeError, IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
def getProfileInfo(profile):
"""
(pyCMS) Gets the internal product information for the given profile.
If profile isn't a valid CmsProfile object or filename to a profile,
a PyCMSError is raised.
If an error occurs while trying to obtain the info tag, a PyCMSError
is raised
Use this function to obtain the information stored in the profile's
info tag. This often contains details about the profile, and how it
was created, as supplied by the creator.
:param profile: EITHER a valid CmsProfile object, OR a string of the
filename of an ICC profile.
:returns: A string containing the internal profile information stored in
an ICC tag.
:exception PyCMSError:
"""
try:
if not isinstance(profile, ImageCmsProfile):
profile = ImageCmsProfile(profile)
# add an extra newline to preserve pyCMS compatibility
# Python, not C. the white point bits weren't working well,
# so skipping.
# info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint
description = profile.profile.product_description
cpright = profile.profile.product_copyright
arr = []
for elt in (description, cpright):
if elt:
arr.append(elt)
return "\r\n\r\n".join(arr) + "\r\n\r\n"
except (AttributeError, IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
def getProfileCopyright(profile):
"""
(pyCMS) Gets the copyright for the given profile.
If profile isn't a valid CmsProfile object or filename to a profile,
a PyCMSError is raised.
If an error occurs while trying to obtain the copyright tag, a PyCMSError
is raised
Use this function to obtain the information stored in the profile's
copyright tag.
:param profile: EITHER a valid CmsProfile object, OR a string of the
filename of an ICC profile.
:returns: A string containing the internal profile information stored in
an ICC tag.
:exception PyCMSError:
"""
try:
# add an extra newline to preserve pyCMS compatibility
if not isinstance(profile, ImageCmsProfile):
profile = ImageCmsProfile(profile)
return profile.profile.product_copyright + "\n"
except (AttributeError, IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
def getProfileManufacturer(profile):
"""
(pyCMS) Gets the manufacturer for the given profile.
If profile isn't a valid CmsProfile object or filename to a profile,
a PyCMSError is raised.
If an error occurs while trying to obtain the manufacturer tag, a
PyCMSError is raised
Use this function to obtain the information stored in the profile's
manufacturer tag.
:param profile: EITHER a valid CmsProfile object, OR a string of the
filename of an ICC profile.
:returns: A string containing the internal profile information stored in
an ICC tag.
:exception PyCMSError:
"""
try:
# add an extra newline to preserve pyCMS compatibility
if not isinstance(profile, ImageCmsProfile):
profile = ImageCmsProfile(profile)
return profile.profile.product_manufacturer + "\n"
except (AttributeError, IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
def getProfileModel(profile):
"""
(pyCMS) Gets the model for the given profile.
If profile isn't a valid CmsProfile object or filename to a profile,
a PyCMSError is raised.
If an error occurs while trying to obtain the model tag, a PyCMSError
is raised
Use this function to obtain the information stored in the profile's
model tag.
:param profile: EITHER a valid CmsProfile object, OR a string of the
filename of an ICC profile.
:returns: A string containing the internal profile information stored in
an ICC tag.
:exception PyCMSError:
"""
try:
# add an extra newline to preserve pyCMS compatibility
if not isinstance(profile, ImageCmsProfile):
profile = ImageCmsProfile(profile)
return profile.profile.product_model + "\n"
except (AttributeError, IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
def getProfileDescription(profile):
"""
(pyCMS) Gets the description for the given profile.
If profile isn't a valid CmsProfile object or filename to a profile,
a PyCMSError is raised.
If an error occurs while trying to obtain the description tag, a PyCMSError
is raised
Use this function to obtain the information stored in the profile's
description tag.
:param profile: EITHER a valid CmsProfile object, OR a string of the
filename of an ICC profile.
:returns: A string containing the internal profile information stored in an
ICC tag.
:exception PyCMSError:
"""
try:
# add an extra newline to preserve pyCMS compatibility
if not isinstance(profile, ImageCmsProfile):
profile = ImageCmsProfile(profile)
return profile.profile.product_description + "\n"
except (AttributeError, IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
def getDefaultIntent(profile):
"""
(pyCMS) Gets the default intent name for the given profile.
If profile isn't a valid CmsProfile object or filename to a profile,
a PyCMSError is raised.
If an error occurs while trying to obtain the default intent, a
PyCMSError is raised.
Use this function to determine the default (and usually best optimized)
rendering intent for this profile. Most profiles support multiple
rendering intents, but are intended mostly for one type of conversion.
If you wish to use a different intent than returned, use
ImageCms.isIntentSupported() to verify it will work first.
:param profile: EITHER a valid CmsProfile object, OR a string of the
filename of an ICC profile.
:returns: Integer 0-3 specifying the default rendering intent for this
profile.
ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT)
ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1
ImageCms.INTENT_SATURATION = 2
ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3
see the pyCMS documentation for details on rendering intents and what
they do.
:exception PyCMSError:
"""
try:
if not isinstance(profile, ImageCmsProfile):
profile = ImageCmsProfile(profile)
return profile.profile.rendering_intent
except (AttributeError, IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
def isIntentSupported(profile, intent, direction):
"""
(pyCMS) Checks if a given intent is supported.
Use this function to verify that you can use your desired
renderingIntent with profile, and that profile can be used for the
input/output/proof profile as you desire.
Some profiles are created specifically for one "direction", can cannot
be used for others. Some profiles can only be used for certain
rendering intents... so it's best to either verify this before trying
to create a transform with them (using this function), or catch the
potential PyCMSError that will occur if they don't support the modes
you select.
:param profile: EITHER a valid CmsProfile object, OR a string of the
filename of an ICC profile.
:param intent: Integer (0-3) specifying the rendering intent you wish to
use with this profile
ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT)
ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1
ImageCms.INTENT_SATURATION = 2
ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3
see the pyCMS documentation for details on rendering intents and what
they do.
:param direction: Integer specifying if the profile is to be used for
input, output, or proof
INPUT = 0 (or use ImageCms.DIRECTION_INPUT)
OUTPUT = 1 (or use ImageCms.DIRECTION_OUTPUT)
PROOF = 2 (or use ImageCms.DIRECTION_PROOF)
:returns: 1 if the intent/direction are supported, -1 if they are not.
:exception PyCMSError:
"""
try:
if not isinstance(profile, ImageCmsProfile):
profile = ImageCmsProfile(profile)
# FIXME: I get different results for the same data w. different
# compilers. Bug in LittleCMS or in the binding?
if profile.profile.is_intent_supported(intent, direction):
return 1
else:
return -1
except (AttributeError, IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
def versions():
"""
(pyCMS) Fetches versions.
"""
return (
VERSION, core.littlecms_version,
sys.version.split()[0], Image.VERSION
)

View File

@ -0,0 +1,315 @@
#
# The Python Imaging Library
# $Id$
#
# map CSS3-style colour description strings to RGB
#
# History:
# 2002-10-24 fl Added support for CSS-style color strings
# 2002-12-15 fl Added RGBA support
# 2004-03-27 fl Fixed remaining int() problems for Python 1.5.2
# 2004-07-19 fl Fixed gray/grey spelling issues
# 2009-03-05 fl Fixed rounding error in grayscale calculation
#
# Copyright (c) 2002-2004 by Secret Labs AB
# Copyright (c) 2002-2004 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
from . import Image
import re
def getrgb(color):
"""
Convert a color string to an RGB tuple. If the string cannot be parsed,
this function raises a :py:exc:`ValueError` exception.
.. versionadded:: 1.1.4
:param color: A color string
:return: ``(red, green, blue[, alpha])``
"""
color = color.lower()
rgb = colormap.get(color, None)
if rgb:
if isinstance(rgb, tuple):
return rgb
colormap[color] = rgb = getrgb(rgb)
return rgb
# check for known string formats
if re.match('#[a-f0-9]{3}$', color):
return (
int(color[1]*2, 16),
int(color[2]*2, 16),
int(color[3]*2, 16),
)
if re.match('#[a-f0-9]{4}$', color):
return (
int(color[1]*2, 16),
int(color[2]*2, 16),
int(color[3]*2, 16),
int(color[4]*2, 16),
)
if re.match('#[a-f0-9]{6}$', color):
return (
int(color[1:3], 16),
int(color[3:5], 16),
int(color[5:7], 16),
)
if re.match('#[a-f0-9]{8}$', color):
return (
int(color[1:3], 16),
int(color[3:5], 16),
int(color[5:7], 16),
int(color[7:9], 16),
)
m = re.match(r"rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color)
if m:
return (
int(m.group(1)),
int(m.group(2)),
int(m.group(3))
)
m = re.match(r"rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color)
if m:
return (
int((int(m.group(1)) * 255) / 100.0 + 0.5),
int((int(m.group(2)) * 255) / 100.0 + 0.5),
int((int(m.group(3)) * 255) / 100.0 + 0.5)
)
m = re.match(
r"hsl\(\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*\)$",
color,
)
if m:
from colorsys import hls_to_rgb
rgb = hls_to_rgb(
float(m.group(1)) / 360.0,
float(m.group(3)) / 100.0,
float(m.group(2)) / 100.0,
)
return (
int(rgb[0] * 255 + 0.5),
int(rgb[1] * 255 + 0.5),
int(rgb[2] * 255 + 0.5)
)
m = re.match(
r"hs[bv]\(\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*\)$",
color,
)
if m:
from colorsys import hsv_to_rgb
rgb = hsv_to_rgb(
float(m.group(1)) / 360.0,
float(m.group(2)) / 100.0,
float(m.group(3)) / 100.0,
)
return (
int(rgb[0] * 255 + 0.5),
int(rgb[1] * 255 + 0.5),
int(rgb[2] * 255 + 0.5)
)
m = re.match(r"rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$",
color)
if m:
return (
int(m.group(1)),
int(m.group(2)),
int(m.group(3)),
int(m.group(4))
)
raise ValueError("unknown color specifier: %r" % color)
def getcolor(color, mode):
"""
Same as :py:func:`~PIL.ImageColor.getrgb`, but converts the RGB value to a
greyscale value if the mode is not color or a palette image. If the string
cannot be parsed, this function raises a :py:exc:`ValueError` exception.
.. versionadded:: 1.1.4
:param color: A color string
:return: ``(graylevel [, alpha]) or (red, green, blue[, alpha])``
"""
# same as getrgb, but converts the result to the given mode
color, alpha = getrgb(color), 255
if len(color) == 4:
color, alpha = color[0:3], color[3]
if Image.getmodebase(mode) == "L":
r, g, b = color
color = (r*299 + g*587 + b*114)//1000
if mode[-1] == 'A':
return (color, alpha)
else:
if mode[-1] == 'A':
return color + (alpha,)
return color
colormap = {
# X11 colour table from https://drafts.csswg.org/css-color-4/, with
# gray/grey spelling issues fixed. This is a superset of HTML 4.0
# colour names used in CSS 1.
"aliceblue": "#f0f8ff",
"antiquewhite": "#faebd7",
"aqua": "#00ffff",
"aquamarine": "#7fffd4",
"azure": "#f0ffff",
"beige": "#f5f5dc",
"bisque": "#ffe4c4",
"black": "#000000",
"blanchedalmond": "#ffebcd",
"blue": "#0000ff",
"blueviolet": "#8a2be2",
"brown": "#a52a2a",
"burlywood": "#deb887",
"cadetblue": "#5f9ea0",
"chartreuse": "#7fff00",
"chocolate": "#d2691e",
"coral": "#ff7f50",
"cornflowerblue": "#6495ed",
"cornsilk": "#fff8dc",
"crimson": "#dc143c",
"cyan": "#00ffff",
"darkblue": "#00008b",
"darkcyan": "#008b8b",
"darkgoldenrod": "#b8860b",
"darkgray": "#a9a9a9",
"darkgrey": "#a9a9a9",
"darkgreen": "#006400",
"darkkhaki": "#bdb76b",
"darkmagenta": "#8b008b",
"darkolivegreen": "#556b2f",
"darkorange": "#ff8c00",
"darkorchid": "#9932cc",
"darkred": "#8b0000",
"darksalmon": "#e9967a",
"darkseagreen": "#8fbc8f",
"darkslateblue": "#483d8b",
"darkslategray": "#2f4f4f",
"darkslategrey": "#2f4f4f",
"darkturquoise": "#00ced1",
"darkviolet": "#9400d3",
"deeppink": "#ff1493",
"deepskyblue": "#00bfff",
"dimgray": "#696969",
"dimgrey": "#696969",
"dodgerblue": "#1e90ff",
"firebrick": "#b22222",
"floralwhite": "#fffaf0",
"forestgreen": "#228b22",
"fuchsia": "#ff00ff",
"gainsboro": "#dcdcdc",
"ghostwhite": "#f8f8ff",
"gold": "#ffd700",
"goldenrod": "#daa520",
"gray": "#808080",
"grey": "#808080",
"green": "#008000",
"greenyellow": "#adff2f",
"honeydew": "#f0fff0",
"hotpink": "#ff69b4",
"indianred": "#cd5c5c",
"indigo": "#4b0082",
"ivory": "#fffff0",
"khaki": "#f0e68c",
"lavender": "#e6e6fa",
"lavenderblush": "#fff0f5",
"lawngreen": "#7cfc00",
"lemonchiffon": "#fffacd",
"lightblue": "#add8e6",
"lightcoral": "#f08080",
"lightcyan": "#e0ffff",
"lightgoldenrodyellow": "#fafad2",
"lightgreen": "#90ee90",
"lightgray": "#d3d3d3",
"lightgrey": "#d3d3d3",
"lightpink": "#ffb6c1",
"lightsalmon": "#ffa07a",
"lightseagreen": "#20b2aa",
"lightskyblue": "#87cefa",
"lightslategray": "#778899",
"lightslategrey": "#778899",
"lightsteelblue": "#b0c4de",
"lightyellow": "#ffffe0",
"lime": "#00ff00",
"limegreen": "#32cd32",
"linen": "#faf0e6",
"magenta": "#ff00ff",
"maroon": "#800000",
"mediumaquamarine": "#66cdaa",
"mediumblue": "#0000cd",
"mediumorchid": "#ba55d3",
"mediumpurple": "#9370db",
"mediumseagreen": "#3cb371",
"mediumslateblue": "#7b68ee",
"mediumspringgreen": "#00fa9a",
"mediumturquoise": "#48d1cc",
"mediumvioletred": "#c71585",
"midnightblue": "#191970",
"mintcream": "#f5fffa",
"mistyrose": "#ffe4e1",
"moccasin": "#ffe4b5",
"navajowhite": "#ffdead",
"navy": "#000080",
"oldlace": "#fdf5e6",
"olive": "#808000",
"olivedrab": "#6b8e23",
"orange": "#ffa500",
"orangered": "#ff4500",
"orchid": "#da70d6",
"palegoldenrod": "#eee8aa",
"palegreen": "#98fb98",
"paleturquoise": "#afeeee",
"palevioletred": "#db7093",
"papayawhip": "#ffefd5",
"peachpuff": "#ffdab9",
"peru": "#cd853f",
"pink": "#ffc0cb",
"plum": "#dda0dd",
"powderblue": "#b0e0e6",
"purple": "#800080",
"rebeccapurple": "#663399",
"red": "#ff0000",
"rosybrown": "#bc8f8f",
"royalblue": "#4169e1",
"saddlebrown": "#8b4513",
"salmon": "#fa8072",
"sandybrown": "#f4a460",
"seagreen": "#2e8b57",
"seashell": "#fff5ee",
"sienna": "#a0522d",
"silver": "#c0c0c0",
"skyblue": "#87ceeb",
"slateblue": "#6a5acd",
"slategray": "#708090",
"slategrey": "#708090",
"snow": "#fffafa",
"springgreen": "#00ff7f",
"steelblue": "#4682b4",
"tan": "#d2b48c",
"teal": "#008080",
"thistle": "#d8bfd8",
"tomato": "#ff6347",
"turquoise": "#40e0d0",
"violet": "#ee82ee",
"wheat": "#f5deb3",
"white": "#ffffff",
"whitesmoke": "#f5f5f5",
"yellow": "#ffff00",
"yellowgreen": "#9acd32",
}

View File

@ -0,0 +1,442 @@
#
# The Python Imaging Library
# $Id$
#
# drawing interface operations
#
# History:
# 1996-04-13 fl Created (experimental)
# 1996-08-07 fl Filled polygons, ellipses.
# 1996-08-13 fl Added text support
# 1998-06-28 fl Handle I and F images
# 1998-12-29 fl Added arc; use arc primitive to draw ellipses
# 1999-01-10 fl Added shape stuff (experimental)
# 1999-02-06 fl Added bitmap support
# 1999-02-11 fl Changed all primitives to take options
# 1999-02-20 fl Fixed backwards compatibility
# 2000-10-12 fl Copy on write, when necessary
# 2001-02-18 fl Use default ink for bitmap/text also in fill mode
# 2002-10-24 fl Added support for CSS-style color strings
# 2002-12-10 fl Added experimental support for RGBA-on-RGB drawing
# 2002-12-11 fl Refactored low-level drawing API (work in progress)
# 2004-08-26 fl Made Draw() a factory function, added getdraw() support
# 2004-09-04 fl Added width support to line primitive
# 2004-09-10 fl Added font mode handling
# 2006-06-19 fl Added font bearing support (getmask2)
#
# Copyright (c) 1997-2006 by Secret Labs AB
# Copyright (c) 1996-2006 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
import math
import numbers
from . import Image, ImageColor
from ._util import isStringType
"""
A simple 2D drawing interface for PIL images.
<p>
Application code should use the <b>Draw</b> factory, instead of
directly.
"""
class ImageDraw(object):
def __init__(self, im, mode=None):
"""
Create a drawing instance.
:param im: The image to draw in.
:param mode: Optional mode to use for color values. For RGB
images, this argument can be RGB or RGBA (to blend the
drawing into the image). For all other modes, this argument
must be the same as the image mode. If omitted, the mode
defaults to the mode of the image.
"""
im.load()
if im.readonly:
im._copy() # make it writeable
blend = 0
if mode is None:
mode = im.mode
if mode != im.mode:
if mode == "RGBA" and im.mode == "RGB":
blend = 1
else:
raise ValueError("mode mismatch")
if mode == "P":
self.palette = im.palette
else:
self.palette = None
self.im = im.im
self.draw = Image.core.draw(self.im, blend)
self.mode = mode
if mode in ("I", "F"):
self.ink = self.draw.draw_ink(1, mode)
else:
self.ink = self.draw.draw_ink(-1, mode)
if mode in ("1", "P", "I", "F"):
# FIXME: fix Fill2 to properly support matte for I+F images
self.fontmode = "1"
else:
self.fontmode = "L" # aliasing is okay for other modes
self.fill = 0
self.font = None
def getfont(self):
"""
Get the current default font.
:returns: An image font."""
if not self.font:
# FIXME: should add a font repository
from . import ImageFont
self.font = ImageFont.load_default()
return self.font
def _getink(self, ink, fill=None):
if ink is None and fill is None:
if self.fill:
fill = self.ink
else:
ink = self.ink
else:
if ink is not None:
if isStringType(ink):
ink = ImageColor.getcolor(ink, self.mode)
if self.palette and not isinstance(ink, numbers.Number):
ink = self.palette.getcolor(ink)
ink = self.draw.draw_ink(ink, self.mode)
if fill is not None:
if isStringType(fill):
fill = ImageColor.getcolor(fill, self.mode)
if self.palette and not isinstance(fill, numbers.Number):
fill = self.palette.getcolor(fill)
fill = self.draw.draw_ink(fill, self.mode)
return ink, fill
def arc(self, xy, start, end, fill=None, width=0):
"""Draw an arc."""
ink, fill = self._getink(fill)
if ink is not None:
self.draw.draw_arc(xy, start, end, ink, width)
def bitmap(self, xy, bitmap, fill=None):
"""Draw a bitmap."""
bitmap.load()
ink, fill = self._getink(fill)
if ink is None:
ink = fill
if ink is not None:
self.draw.draw_bitmap(xy, bitmap.im, ink)
def chord(self, xy, start, end, fill=None, outline=None, width=0):
"""Draw a chord."""
ink, fill = self._getink(outline, fill)
if fill is not None:
self.draw.draw_chord(xy, start, end, fill, 1)
if ink is not None and ink != fill:
self.draw.draw_chord(xy, start, end, ink, 0, width)
def ellipse(self, xy, fill=None, outline=None, width=0):
"""Draw an ellipse."""
ink, fill = self._getink(outline, fill)
if fill is not None:
self.draw.draw_ellipse(xy, fill, 1)
if ink is not None and ink != fill:
self.draw.draw_ellipse(xy, ink, 0, width)
def line(self, xy, fill=None, width=0, joint=None):
"""Draw a line, or a connected sequence of line segments."""
ink = self._getink(fill)[0]
if ink is not None:
self.draw.draw_lines(xy, ink, width)
if joint == "curve" and width > 4:
for i in range(1, len(xy)-1):
point = xy[i]
angles = [
math.degrees(math.atan2(
end[0] - start[0], start[1] - end[1]
)) % 360
for start, end in ((xy[i-1], point), (point, xy[i+1]))
]
if angles[0] == angles[1]:
# This is a straight line, so no joint is required
continue
def coord_at_angle(coord, angle):
x, y = coord
angle -= 90
distance = width/2 - 1
return tuple([
p +
(math.floor(p_d) if p_d > 0 else math.ceil(p_d))
for p, p_d in
((x, distance * math.cos(math.radians(angle))),
(y, distance * math.sin(math.radians(angle))))
])
flipped = ((angles[1] > angles[0] and
angles[1] - 180 > angles[0]) or
(angles[1] < angles[0] and
angles[1] + 180 > angles[0]))
coords = [
(point[0] - width/2 + 1, point[1] - width/2 + 1),
(point[0] + width/2 - 1, point[1] + width/2 - 1)
]
if flipped:
start, end = (angles[1] + 90, angles[0] + 90)
else:
start, end = (angles[0] - 90, angles[1] - 90)
self.pieslice(coords, start - 90, end - 90, fill)
if width > 8:
# Cover potential gaps between the line and the joint
if flipped:
gapCoords = [
coord_at_angle(point, angles[0]+90),
point,
coord_at_angle(point, angles[1]+90)
]
else:
gapCoords = [
coord_at_angle(point, angles[0]-90),
point,
coord_at_angle(point, angles[1]-90)
]
self.line(gapCoords, fill, width=3)
def shape(self, shape, fill=None, outline=None):
"""(Experimental) Draw a shape."""
shape.close()
ink, fill = self._getink(outline, fill)
if fill is not None:
self.draw.draw_outline(shape, fill, 1)
if ink is not None and ink != fill:
self.draw.draw_outline(shape, ink, 0)
def pieslice(self, xy, start, end, fill=None, outline=None, width=0):
"""Draw a pieslice."""
ink, fill = self._getink(outline, fill)
if fill is not None:
self.draw.draw_pieslice(xy, start, end, fill, 1)
if ink is not None and ink != fill:
self.draw.draw_pieslice(xy, start, end, ink, 0, width)
def point(self, xy, fill=None):
"""Draw one or more individual pixels."""
ink, fill = self._getink(fill)
if ink is not None:
self.draw.draw_points(xy, ink)
def polygon(self, xy, fill=None, outline=None):
"""Draw a polygon."""
ink, fill = self._getink(outline, fill)
if fill is not None:
self.draw.draw_polygon(xy, fill, 1)
if ink is not None and ink != fill:
self.draw.draw_polygon(xy, ink, 0)
def rectangle(self, xy, fill=None, outline=None, width=0):
"""Draw a rectangle."""
ink, fill = self._getink(outline, fill)
if fill is not None:
self.draw.draw_rectangle(xy, fill, 1)
if ink is not None and ink != fill:
self.draw.draw_rectangle(xy, ink, 0, width)
def _multiline_check(self, text):
"""Draw text."""
split_character = "\n" if isinstance(text, str) else b"\n"
return split_character in text
def _multiline_split(self, text):
split_character = "\n" if isinstance(text, str) else b"\n"
return text.split(split_character)
def text(self, xy, text, fill=None, font=None, anchor=None,
*args, **kwargs):
if self._multiline_check(text):
return self.multiline_text(xy, text, fill, font, anchor,
*args, **kwargs)
ink, fill = self._getink(fill)
if font is None:
font = self.getfont()
if ink is None:
ink = fill
if ink is not None:
try:
mask, offset = font.getmask2(text, self.fontmode,
*args, **kwargs)
xy = xy[0] + offset[0], xy[1] + offset[1]
except AttributeError:
try:
mask = font.getmask(text, self.fontmode, *args, **kwargs)
except TypeError:
mask = font.getmask(text)
self.draw.draw_bitmap(xy, mask, ink)
def multiline_text(self, xy, text, fill=None, font=None, anchor=None,
spacing=4, align="left", direction=None, features=None):
widths = []
max_width = 0
lines = self._multiline_split(text)
line_spacing = self.textsize('A', font=font)[1] + spacing
for line in lines:
line_width, line_height = self.textsize(line, font)
widths.append(line_width)
max_width = max(max_width, line_width)
left, top = xy
for idx, line in enumerate(lines):
if align == "left":
pass # left = x
elif align == "center":
left += (max_width - widths[idx]) / 2.0
elif align == "right":
left += (max_width - widths[idx])
else:
raise ValueError('align must be "left", "center" or "right"')
self.text((left, top), line, fill, font, anchor,
direction=direction, features=features)
top += line_spacing
left = xy[0]
def textsize(self, text, font=None, spacing=4, direction=None,
features=None):
"""Get the size of a given string, in pixels."""
if self._multiline_check(text):
return self.multiline_textsize(text, font, spacing,
direction, features)
if font is None:
font = self.getfont()
return font.getsize(text, direction, features)
def multiline_textsize(self, text, font=None, spacing=4, direction=None,
features=None):
max_width = 0
lines = self._multiline_split(text)
line_spacing = self.textsize('A', font=font)[1] + spacing
for line in lines:
line_width, line_height = self.textsize(line, font, spacing,
direction, features)
max_width = max(max_width, line_width)
return max_width, len(lines)*line_spacing - spacing
def Draw(im, mode=None):
"""
A simple 2D drawing interface for PIL images.
:param im: The image to draw in.
:param mode: Optional mode to use for color values. For RGB
images, this argument can be RGB or RGBA (to blend the
drawing into the image). For all other modes, this argument
must be the same as the image mode. If omitted, the mode
defaults to the mode of the image.
"""
try:
return im.getdraw(mode)
except AttributeError:
return ImageDraw(im, mode)
# experimental access to the outline API
try:
Outline = Image.core.outline
except AttributeError:
Outline = None
def getdraw(im=None, hints=None):
"""
(Experimental) A more advanced 2D drawing interface for PIL images,
based on the WCK interface.
:param im: The image to draw in.
:param hints: An optional list of hints.
:returns: A (drawing context, drawing resource factory) tuple.
"""
# FIXME: this needs more work!
# FIXME: come up with a better 'hints' scheme.
handler = None
if not hints or "nicest" in hints:
try:
from . import _imagingagg as handler
except ImportError:
pass
if handler is None:
from . import ImageDraw2 as handler
if im:
im = handler.Draw(im)
return im, handler
def floodfill(image, xy, value, border=None, thresh=0):
"""
(experimental) Fills a bounded region with a given color.
:param image: Target image.
:param xy: Seed position (a 2-item coordinate tuple). See
:ref:`coordinate-system`.
:param value: Fill color.
:param border: Optional border value. If given, the region consists of
pixels with a color different from the border color. If not given,
the region consists of pixels having the same color as the seed
pixel.
:param thresh: Optional threshold value which specifies a maximum
tolerable difference of a pixel value from the 'background' in
order for it to be replaced. Useful for filling regions of
non-homogeneous, but similar, colors.
"""
# based on an implementation by Eric S. Raymond
# amended by yo1995 @20180806
pixel = image.load()
x, y = xy
try:
background = pixel[x, y]
if _color_diff(value, background) <= thresh:
return # seed point already has fill color
pixel[x, y] = value
except (ValueError, IndexError):
return # seed point outside image
edge = {(x, y)}
# use a set to keep record of current and previous edge pixels
# to reduce memory consumption
full_edge = set()
while edge:
new_edge = set()
for (x, y) in edge: # 4 adjacent method
for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)):
if (s, t) in full_edge:
continue # if already processed, skip
try:
p = pixel[s, t]
except (ValueError, IndexError):
pass
else:
full_edge.add((s, t))
if border is None:
fill = _color_diff(p, background) <= thresh
else:
fill = p != value and p != border
if fill:
pixel[s, t] = value
new_edge.add((s, t))
full_edge = edge # discard pixels processed
edge = new_edge
def _color_diff(color1, color2):
"""
Uses 1-norm distance to calculate difference between two values.
"""
if isinstance(color2, tuple):
return sum([abs(color1[i]-color2[i]) for i in range(0, len(color2))])
else:
return abs(color1-color2)

View File

@ -0,0 +1,108 @@
#
# The Python Imaging Library
# $Id$
#
# WCK-style drawing interface operations
#
# History:
# 2003-12-07 fl created
# 2005-05-15 fl updated; added to PIL as ImageDraw2
# 2005-05-15 fl added text support
# 2005-05-20 fl added arc/chord/pieslice support
#
# Copyright (c) 2003-2005 by Secret Labs AB
# Copyright (c) 2003-2005 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath
class Pen(object):
def __init__(self, color, width=1, opacity=255):
self.color = ImageColor.getrgb(color)
self.width = width
class Brush(object):
def __init__(self, color, opacity=255):
self.color = ImageColor.getrgb(color)
class Font(object):
def __init__(self, color, file, size=12):
# FIXME: add support for bitmap fonts
self.color = ImageColor.getrgb(color)
self.font = ImageFont.truetype(file, size)
class Draw(object):
def __init__(self, image, size=None, color=None):
if not hasattr(image, "im"):
image = Image.new(image, size, color)
self.draw = ImageDraw.Draw(image)
self.image = image
self.transform = None
def flush(self):
return self.image
def render(self, op, xy, pen, brush=None):
# handle color arguments
outline = fill = None
width = 1
if isinstance(pen, Pen):
outline = pen.color
width = pen.width
elif isinstance(brush, Pen):
outline = brush.color
width = brush.width
if isinstance(brush, Brush):
fill = brush.color
elif isinstance(pen, Brush):
fill = pen.color
# handle transformation
if self.transform:
xy = ImagePath.Path(xy)
xy.transform(self.transform)
# render the item
if op == "line":
self.draw.line(xy, fill=outline, width=width)
else:
getattr(self.draw, op)(xy, fill=fill, outline=outline)
def settransform(self, offset):
(xoffset, yoffset) = offset
self.transform = (1, 0, xoffset, 0, 1, yoffset)
def arc(self, xy, start, end, *options):
self.render("arc", xy, start, end, *options)
def chord(self, xy, start, end, *options):
self.render("chord", xy, start, end, *options)
def ellipse(self, xy, *options):
self.render("ellipse", xy, *options)
def line(self, xy, *options):
self.render("line", xy, *options)
def pieslice(self, xy, start, end, *options):
self.render("pieslice", xy, start, end, *options)
def polygon(self, xy, *options):
self.render("polygon", xy, *options)
def rectangle(self, xy, *options):
self.render("rectangle", xy, *options)
def text(self, xy, text, font):
if self.transform:
xy = ImagePath.Path(xy)
xy.transform(self.transform)
self.draw.text(xy, text, font=font.font, fill=font.color)
def textsize(self, text, font):
return self.draw.textsize(text, font=font.font)

View File

@ -0,0 +1,101 @@
#
# The Python Imaging Library.
# $Id$
#
# image enhancement classes
#
# For a background, see "Image Processing By Interpolation and
# Extrapolation", Paul Haeberli and Douglas Voorhies. Available
# at http://www.graficaobscura.com/interp/index.html
#
# History:
# 1996-03-23 fl Created
# 2009-06-16 fl Fixed mean calculation
#
# Copyright (c) Secret Labs AB 1997.
# Copyright (c) Fredrik Lundh 1996.
#
# See the README file for information on usage and redistribution.
#
from . import Image, ImageFilter, ImageStat
class _Enhance(object):
def enhance(self, factor):
"""
Returns an enhanced image.
:param factor: A floating point value controlling the enhancement.
Factor 1.0 always returns a copy of the original image,
lower factors mean less color (brightness, contrast,
etc), and higher values more. There are no restrictions
on this value.
:rtype: :py:class:`~PIL.Image.Image`
"""
return Image.blend(self.degenerate, self.image, factor)
class Color(_Enhance):
"""Adjust image color balance.
This class can be used to adjust the colour balance of an image, in
a manner similar to the controls on a colour TV set. An enhancement
factor of 0.0 gives a black and white image. A factor of 1.0 gives
the original image.
"""
def __init__(self, image):
self.image = image
self.intermediate_mode = 'L'
if 'A' in image.getbands():
self.intermediate_mode = 'LA'
self.degenerate = image.convert(
self.intermediate_mode).convert(image.mode)
class Contrast(_Enhance):
"""Adjust image contrast.
This class can be used to control the contrast of an image, similar
to the contrast control on a TV set. An enhancement factor of 0.0
gives a solid grey image. A factor of 1.0 gives the original image.
"""
def __init__(self, image):
self.image = image
mean = int(ImageStat.Stat(image.convert("L")).mean[0] + 0.5)
self.degenerate = Image.new("L", image.size, mean).convert(image.mode)
if 'A' in image.getbands():
self.degenerate.putalpha(image.getchannel('A'))
class Brightness(_Enhance):
"""Adjust image brightness.
This class can be used to control the brightness of an image. An
enhancement factor of 0.0 gives a black image. A factor of 1.0 gives the
original image.
"""
def __init__(self, image):
self.image = image
self.degenerate = Image.new(image.mode, image.size, 0)
if 'A' in image.getbands():
self.degenerate.putalpha(image.getchannel('A'))
class Sharpness(_Enhance):
"""Adjust image sharpness.
This class can be used to adjust the sharpness of an image. An
enhancement factor of 0.0 gives a blurred image, a factor of 1.0 gives the
original image, and a factor of 2.0 gives a sharpened image.
"""
def __init__(self, image):
self.image = image
self.degenerate = image.filter(ImageFilter.SMOOTH)
if 'A' in image.getbands():
self.degenerate.putalpha(image.getchannel('A'))

View File

@ -0,0 +1,675 @@
#
# The Python Imaging Library.
# $Id$
#
# base class for image file handlers
#
# history:
# 1995-09-09 fl Created
# 1996-03-11 fl Fixed load mechanism.
# 1996-04-15 fl Added pcx/xbm decoders.
# 1996-04-30 fl Added encoders.
# 1996-12-14 fl Added load helpers
# 1997-01-11 fl Use encode_to_file where possible
# 1997-08-27 fl Flush output in _save
# 1998-03-05 fl Use memory mapping for some modes
# 1999-02-04 fl Use memory mapping also for "I;16" and "I;16B"
# 1999-05-31 fl Added image parser
# 2000-10-12 fl Set readonly flag on memory-mapped images
# 2002-03-20 fl Use better messages for common decoder errors
# 2003-04-21 fl Fall back on mmap/map_buffer if map is not available
# 2003-10-30 fl Added StubImageFile class
# 2004-02-25 fl Made incremental parser more robust
#
# Copyright (c) 1997-2004 by Secret Labs AB
# Copyright (c) 1995-2004 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
from . import Image
from ._util import isPath
import io
import sys
import struct
MAXBLOCK = 65536
SAFEBLOCK = 1024*1024
LOAD_TRUNCATED_IMAGES = False
ERRORS = {
-1: "image buffer overrun error",
-2: "decoding error",
-3: "unknown error",
-8: "bad configuration",
-9: "out of memory error"
}
def raise_ioerror(error):
try:
message = Image.core.getcodecstatus(error)
except AttributeError:
message = ERRORS.get(error)
if not message:
message = "decoder error %d" % error
raise IOError(message + " when reading image file")
#
# --------------------------------------------------------------------
# Helpers
def _tilesort(t):
# sort on offset
return t[2]
#
# --------------------------------------------------------------------
# ImageFile base class
class ImageFile(Image.Image):
"Base class for image file format handlers."
def __init__(self, fp=None, filename=None):
Image.Image.__init__(self)
self._min_frame = 0
self.custom_mimetype = None
self.tile = None
self.readonly = 1 # until we know better
self.decoderconfig = ()
self.decodermaxblock = MAXBLOCK
if isPath(fp):
# filename
self.fp = open(fp, "rb")
self.filename = fp
self._exclusive_fp = True
else:
# stream
self.fp = fp
self.filename = filename
# can be overridden
self._exclusive_fp = None
try:
self._open()
except (IndexError, # end of data
TypeError, # end of data (ord)
KeyError, # unsupported mode
EOFError, # got header but not the first frame
struct.error) as v:
# close the file only if we have opened it this constructor
if self._exclusive_fp:
self.fp.close()
raise SyntaxError(v)
if not self.mode or self.size[0] <= 0:
raise SyntaxError("not identified by this driver")
def draft(self, mode, size):
"Set draft mode"
pass
def get_format_mimetype(self):
if self.format is None:
return
return self.custom_mimetype or Image.MIME.get(self.format.upper())
def verify(self):
"Check file integrity"
# raise exception if something's wrong. must be called
# directly after open, and closes file when finished.
if self._exclusive_fp:
self.fp.close()
self.fp = None
def load(self):
"Load image data based on tile list"
pixel = Image.Image.load(self)
if self.tile is None:
raise IOError("cannot load this image")
if not self.tile:
return pixel
self.map = None
use_mmap = self.filename and len(self.tile) == 1
# As of pypy 2.1.0, memory mapping was failing here.
use_mmap = use_mmap and not hasattr(sys, 'pypy_version_info')
readonly = 0
# look for read/seek overrides
try:
read = self.load_read
# don't use mmap if there are custom read/seek functions
use_mmap = False
except AttributeError:
read = self.fp.read
try:
seek = self.load_seek
use_mmap = False
except AttributeError:
seek = self.fp.seek
if use_mmap:
# try memory mapping
decoder_name, extents, offset, args = self.tile[0]
if decoder_name == "raw" and len(args) >= 3 and \
args[0] == self.mode and \
args[0] in Image._MAPMODES:
try:
if hasattr(Image.core, "map"):
# use built-in mapper WIN32 only
self.map = Image.core.map(self.filename)
self.map.seek(offset)
self.im = self.map.readimage(
self.mode, self.size, args[1], args[2]
)
else:
# use mmap, if possible
import mmap
with open(self.filename, "r") as fp:
self.map = mmap.mmap(fp.fileno(), 0,
access=mmap.ACCESS_READ)
self.im = Image.core.map_buffer(
self.map, self.size, decoder_name, extents,
offset, args)
readonly = 1
# After trashing self.im,
# we might need to reload the palette data.
if self.palette:
self.palette.dirty = 1
except (AttributeError, EnvironmentError, ImportError):
self.map = None
self.load_prepare()
err_code = -3 # initialize to unknown error
if not self.map:
# sort tiles in file order
self.tile.sort(key=_tilesort)
try:
# FIXME: This is a hack to handle TIFF's JpegTables tag.
prefix = self.tile_prefix
except AttributeError:
prefix = b""
for decoder_name, extents, offset, args in self.tile:
decoder = Image._getdecoder(self.mode, decoder_name,
args, self.decoderconfig)
try:
seek(offset)
decoder.setimage(self.im, extents)
if decoder.pulls_fd:
decoder.setfd(self.fp)
status, err_code = decoder.decode(b"")
else:
b = prefix
while True:
try:
s = read(self.decodermaxblock)
except (IndexError, struct.error):
# truncated png/gif
if LOAD_TRUNCATED_IMAGES:
break
else:
raise IOError("image file is truncated")
if not s: # truncated jpeg
if LOAD_TRUNCATED_IMAGES:
break
else:
self.tile = []
raise IOError("image file is truncated "
"(%d bytes not processed)" %
len(b))
b = b + s
n, err_code = decoder.decode(b)
if n < 0:
break
b = b[n:]
finally:
# Need to cleanup here to prevent leaks
decoder.cleanup()
self.tile = []
self.readonly = readonly
self.load_end()
if self._exclusive_fp and self._close_exclusive_fp_after_loading:
self.fp.close()
self.fp = None
if not self.map and not LOAD_TRUNCATED_IMAGES and err_code < 0:
# still raised if decoder fails to return anything
raise_ioerror(err_code)
return Image.Image.load(self)
def load_prepare(self):
# create image memory if necessary
if not self.im or\
self.im.mode != self.mode or self.im.size != self.size:
self.im = Image.core.new(self.mode, self.size)
# create palette (optional)
if self.mode == "P":
Image.Image.load(self)
def load_end(self):
# may be overridden
pass
# may be defined for contained formats
# def load_seek(self, pos):
# pass
# may be defined for blocked formats (e.g. PNG)
# def load_read(self, bytes):
# pass
def _seek_check(self, frame):
if (frame < self._min_frame or
# Only check upper limit on frames if additional seek operations
# are not required to do so
(not (hasattr(self, "_n_frames") and self._n_frames is None) and
frame >= self.n_frames+self._min_frame)):
raise EOFError("attempt to seek outside sequence")
return self.tell() != frame
class StubImageFile(ImageFile):
"""
Base class for stub image loaders.
A stub loader is an image loader that can identify files of a
certain format, but relies on external code to load the file.
"""
def _open(self):
raise NotImplementedError(
"StubImageFile subclass must implement _open"
)
def load(self):
loader = self._load()
if loader is None:
raise IOError("cannot find loader for this %s file" % self.format)
image = loader.load(self)
assert image is not None
# become the other object (!)
self.__class__ = image.__class__
self.__dict__ = image.__dict__
def _load(self):
"(Hook) Find actual image loader."
raise NotImplementedError(
"StubImageFile subclass must implement _load"
)
class Parser(object):
"""
Incremental image parser. This class implements the standard
feed/close consumer interface.
"""
incremental = None
image = None
data = None
decoder = None
offset = 0
finished = 0
def reset(self):
"""
(Consumer) Reset the parser. Note that you can only call this
method immediately after you've created a parser; parser
instances cannot be reused.
"""
assert self.data is None, "cannot reuse parsers"
def feed(self, data):
"""
(Consumer) Feed data to the parser.
:param data: A string buffer.
:exception IOError: If the parser failed to parse the image file.
"""
# collect data
if self.finished:
return
if self.data is None:
self.data = data
else:
self.data = self.data + data
# parse what we have
if self.decoder:
if self.offset > 0:
# skip header
skip = min(len(self.data), self.offset)
self.data = self.data[skip:]
self.offset = self.offset - skip
if self.offset > 0 or not self.data:
return
n, e = self.decoder.decode(self.data)
if n < 0:
# end of stream
self.data = None
self.finished = 1
if e < 0:
# decoding error
self.image = None
raise_ioerror(e)
else:
# end of image
return
self.data = self.data[n:]
elif self.image:
# if we end up here with no decoder, this file cannot
# be incrementally parsed. wait until we've gotten all
# available data
pass
else:
# attempt to open this file
try:
with io.BytesIO(self.data) as fp:
im = Image.open(fp)
except IOError:
# traceback.print_exc()
pass # not enough data
else:
flag = hasattr(im, "load_seek") or hasattr(im, "load_read")
if flag or len(im.tile) != 1:
# custom load code, or multiple tiles
self.decode = None
else:
# initialize decoder
im.load_prepare()
d, e, o, a = im.tile[0]
im.tile = []
self.decoder = Image._getdecoder(
im.mode, d, a, im.decoderconfig
)
self.decoder.setimage(im.im, e)
# calculate decoder offset
self.offset = o
if self.offset <= len(self.data):
self.data = self.data[self.offset:]
self.offset = 0
self.image = im
def __enter__(self):
return self
def __exit__(self, *args):
self.close()
def close(self):
"""
(Consumer) Close the stream.
:returns: An image object.
:exception IOError: If the parser failed to parse the image file either
because it cannot be identified or cannot be
decoded.
"""
# finish decoding
if self.decoder:
# get rid of what's left in the buffers
self.feed(b"")
self.data = self.decoder = None
if not self.finished:
raise IOError("image was incomplete")
if not self.image:
raise IOError("cannot parse this image")
if self.data:
# incremental parsing not possible; reopen the file
# not that we have all data
with io.BytesIO(self.data) as fp:
try:
self.image = Image.open(fp)
finally:
self.image.load()
return self.image
# --------------------------------------------------------------------
def _save(im, fp, tile, bufsize=0):
"""Helper to save image based on tile list
:param im: Image object.
:param fp: File object.
:param tile: Tile list.
:param bufsize: Optional buffer size
"""
im.load()
if not hasattr(im, "encoderconfig"):
im.encoderconfig = ()
tile.sort(key=_tilesort)
# FIXME: make MAXBLOCK a configuration parameter
# It would be great if we could have the encoder specify what it needs
# But, it would need at least the image size in most cases. RawEncode is
# a tricky case.
bufsize = max(MAXBLOCK, bufsize, im.size[0] * 4) # see RawEncode.c
if fp == sys.stdout:
fp.flush()
return
try:
fh = fp.fileno()
fp.flush()
except (AttributeError, io.UnsupportedOperation):
# compress to Python file-compatible object
for e, b, o, a in tile:
e = Image._getencoder(im.mode, e, a, im.encoderconfig)
if o > 0:
fp.seek(o, 0)
e.setimage(im.im, b)
if e.pushes_fd:
e.setfd(fp)
l, s = e.encode_to_pyfd()
else:
while True:
l, s, d = e.encode(bufsize)
fp.write(d)
if s:
break
if s < 0:
raise IOError("encoder error %d when writing image file" % s)
e.cleanup()
else:
# slight speedup: compress to real file object
for e, b, o, a in tile:
e = Image._getencoder(im.mode, e, a, im.encoderconfig)
if o > 0:
fp.seek(o, 0)
e.setimage(im.im, b)
if e.pushes_fd:
e.setfd(fp)
l, s = e.encode_to_pyfd()
else:
s = e.encode_to_file(fh, bufsize)
if s < 0:
raise IOError("encoder error %d when writing image file" % s)
e.cleanup()
if hasattr(fp, "flush"):
fp.flush()
def _safe_read(fp, size):
"""
Reads large blocks in a safe way. Unlike fp.read(n), this function
doesn't trust the user. If the requested size is larger than
SAFEBLOCK, the file is read block by block.
:param fp: File handle. Must implement a <b>read</b> method.
:param size: Number of bytes to read.
:returns: A string containing up to <i>size</i> bytes of data.
"""
if size <= 0:
return b""
if size <= SAFEBLOCK:
return fp.read(size)
data = []
while size > 0:
block = fp.read(min(size, SAFEBLOCK))
if not block:
break
data.append(block)
size -= len(block)
return b"".join(data)
class PyCodecState(object):
def __init__(self):
self.xsize = 0
self.ysize = 0
self.xoff = 0
self.yoff = 0
def extents(self):
return (self.xoff, self.yoff,
self.xoff+self.xsize, self.yoff+self.ysize)
class PyDecoder(object):
"""
Python implementation of a format decoder. Override this class and
add the decoding logic in the `decode` method.
See :ref:`Writing Your Own File Decoder in Python<file-decoders-py>`
"""
_pulls_fd = False
def __init__(self, mode, *args):
self.im = None
self.state = PyCodecState()
self.fd = None
self.mode = mode
self.init(args)
def init(self, args):
"""
Override to perform decoder specific initialization
:param args: Array of args items from the tile entry
:returns: None
"""
self.args = args
@property
def pulls_fd(self):
return self._pulls_fd
def decode(self, buffer):
"""
Override to perform the decoding process.
:param buffer: A bytes object with the data to be decoded.
If `handles_eof` is set, then `buffer` will be empty and `self.fd`
will be set.
:returns: A tuple of (bytes consumed, errcode).
If finished with decoding return <0 for the bytes consumed.
Err codes are from `ERRORS`
"""
raise NotImplementedError()
def cleanup(self):
"""
Override to perform decoder specific cleanup
:returns: None
"""
pass
def setfd(self, fd):
"""
Called from ImageFile to set the python file-like object
:param fd: A python file-like object
:returns: None
"""
self.fd = fd
def setimage(self, im, extents=None):
"""
Called from ImageFile to set the core output image for the decoder
:param im: A core image object
:param extents: a 4 tuple of (x0, y0, x1, y1) defining the rectangle
for this tile
:returns: None
"""
# following c code
self.im = im
if extents:
(x0, y0, x1, y1) = extents
else:
(x0, y0, x1, y1) = (0, 0, 0, 0)
if x0 == 0 and x1 == 0:
self.state.xsize, self.state.ysize = self.im.size
else:
self.state.xoff = x0
self.state.yoff = y0
self.state.xsize = x1 - x0
self.state.ysize = y1 - y0
if self.state.xsize <= 0 or self.state.ysize <= 0:
raise ValueError("Size cannot be negative")
if (self.state.xsize + self.state.xoff > self.im.size[0] or
self.state.ysize + self.state.yoff > self.im.size[1]):
raise ValueError("Tile cannot extend outside image")
def set_as_raw(self, data, rawmode=None):
"""
Convenience method to set the internal image from a stream of raw data
:param data: Bytes to be set
:param rawmode: The rawmode to be used for the decoder.
If not specified, it will default to the mode of the image
:returns: None
"""
if not rawmode:
rawmode = self.mode
d = Image._getdecoder(self.mode, 'raw', (rawmode))
d.setimage(self.im, self.state.extents())
s = d.decode(data)
if s[0] >= 0:
raise ValueError("not enough image data")
if s[1] != 0:
raise ValueError("cannot decode image data")

View File

@ -0,0 +1,483 @@
#
# The Python Imaging Library.
# $Id$
#
# standard filters
#
# History:
# 1995-11-27 fl Created
# 2002-06-08 fl Added rank and mode filters
# 2003-09-15 fl Fixed rank calculation in rank filter; added expand call
#
# Copyright (c) 1997-2003 by Secret Labs AB.
# Copyright (c) 1995-2002 by Fredrik Lundh.
#
# See the README file for information on usage and redistribution.
#
from __future__ import division
import functools
try:
import numpy
except ImportError: # pragma: no cover
numpy = None
class Filter(object):
pass
class MultibandFilter(Filter):
pass
class BuiltinFilter(MultibandFilter):
def filter(self, image):
if image.mode == "P":
raise ValueError("cannot filter palette images")
return image.filter(*self.filterargs)
class Kernel(BuiltinFilter):
"""
Create a convolution kernel. The current version only
supports 3x3 and 5x5 integer and floating point kernels.
In the current version, kernels can only be applied to
"L" and "RGB" images.
:param size: Kernel size, given as (width, height). In the current
version, this must be (3,3) or (5,5).
:param kernel: A sequence containing kernel weights.
:param scale: Scale factor. If given, the result for each pixel is
divided by this value. the default is the sum of the
kernel weights.
:param offset: Offset. If given, this value is added to the result,
after it has been divided by the scale factor.
"""
name = "Kernel"
def __init__(self, size, kernel, scale=None, offset=0):
if scale is None:
# default scale is sum of kernel
scale = functools.reduce(lambda a, b: a+b, kernel)
if size[0] * size[1] != len(kernel):
raise ValueError("not enough coefficients in kernel")
self.filterargs = size, scale, offset, kernel
class RankFilter(Filter):
"""
Create a rank filter. The rank filter sorts all pixels in
a window of the given size, and returns the **rank**'th value.
:param size: The kernel size, in pixels.
:param rank: What pixel value to pick. Use 0 for a min filter,
``size * size / 2`` for a median filter, ``size * size - 1``
for a max filter, etc.
"""
name = "Rank"
def __init__(self, size, rank):
self.size = size
self.rank = rank
def filter(self, image):
if image.mode == "P":
raise ValueError("cannot filter palette images")
image = image.expand(self.size//2, self.size//2)
return image.rankfilter(self.size, self.rank)
class MedianFilter(RankFilter):
"""
Create a median filter. Picks the median pixel value in a window with the
given size.
:param size: The kernel size, in pixels.
"""
name = "Median"
def __init__(self, size=3):
self.size = size
self.rank = size*size//2
class MinFilter(RankFilter):
"""
Create a min filter. Picks the lowest pixel value in a window with the
given size.
:param size: The kernel size, in pixels.
"""
name = "Min"
def __init__(self, size=3):
self.size = size
self.rank = 0
class MaxFilter(RankFilter):
"""
Create a max filter. Picks the largest pixel value in a window with the
given size.
:param size: The kernel size, in pixels.
"""
name = "Max"
def __init__(self, size=3):
self.size = size
self.rank = size*size-1
class ModeFilter(Filter):
"""
Create a mode filter. Picks the most frequent pixel value in a box with the
given size. Pixel values that occur only once or twice are ignored; if no
pixel value occurs more than twice, the original pixel value is preserved.
:param size: The kernel size, in pixels.
"""
name = "Mode"
def __init__(self, size=3):
self.size = size
def filter(self, image):
return image.modefilter(self.size)
class GaussianBlur(MultibandFilter):
"""Gaussian blur filter.
:param radius: Blur radius.
"""
name = "GaussianBlur"
def __init__(self, radius=2):
self.radius = radius
def filter(self, image):
return image.gaussian_blur(self.radius)
class BoxBlur(MultibandFilter):
"""Blurs the image by setting each pixel to the average value of the pixels
in a square box extending radius pixels in each direction.
Supports float radius of arbitrary size. Uses an optimized implementation
which runs in linear time relative to the size of the image
for any radius value.
:param radius: Size of the box in one direction. Radius 0 does not blur,
returns an identical image. Radius 1 takes 1 pixel
in each direction, i.e. 9 pixels in total.
"""
name = "BoxBlur"
def __init__(self, radius):
self.radius = radius
def filter(self, image):
return image.box_blur(self.radius)
class UnsharpMask(MultibandFilter):
"""Unsharp mask filter.
See Wikipedia's entry on `digital unsharp masking`_ for an explanation of
the parameters.
:param radius: Blur Radius
:param percent: Unsharp strength, in percent
:param threshold: Threshold controls the minimum brightness change that
will be sharpened
.. _digital unsharp masking: https://en.wikipedia.org/wiki/Unsharp_masking#Digital_unsharp_masking
""" # noqa: E501
name = "UnsharpMask"
def __init__(self, radius=2, percent=150, threshold=3):
self.radius = radius
self.percent = percent
self.threshold = threshold
def filter(self, image):
return image.unsharp_mask(self.radius, self.percent, self.threshold)
class BLUR(BuiltinFilter):
name = "Blur"
filterargs = (5, 5), 16, 0, (
1, 1, 1, 1, 1,
1, 0, 0, 0, 1,
1, 0, 0, 0, 1,
1, 0, 0, 0, 1,
1, 1, 1, 1, 1
)
class CONTOUR(BuiltinFilter):
name = "Contour"
filterargs = (3, 3), 1, 255, (
-1, -1, -1,
-1, 8, -1,
-1, -1, -1
)
class DETAIL(BuiltinFilter):
name = "Detail"
filterargs = (3, 3), 6, 0, (
0, -1, 0,
-1, 10, -1,
0, -1, 0
)
class EDGE_ENHANCE(BuiltinFilter):
name = "Edge-enhance"
filterargs = (3, 3), 2, 0, (
-1, -1, -1,
-1, 10, -1,
-1, -1, -1
)
class EDGE_ENHANCE_MORE(BuiltinFilter):
name = "Edge-enhance More"
filterargs = (3, 3), 1, 0, (
-1, -1, -1,
-1, 9, -1,
-1, -1, -1
)
class EMBOSS(BuiltinFilter):
name = "Emboss"
filterargs = (3, 3), 1, 128, (
-1, 0, 0,
0, 1, 0,
0, 0, 0
)
class FIND_EDGES(BuiltinFilter):
name = "Find Edges"
filterargs = (3, 3), 1, 0, (
-1, -1, -1,
-1, 8, -1,
-1, -1, -1
)
class SHARPEN(BuiltinFilter):
name = "Sharpen"
filterargs = (3, 3), 16, 0, (
-2, -2, -2,
-2, 32, -2,
-2, -2, -2
)
class SMOOTH(BuiltinFilter):
name = "Smooth"
filterargs = (3, 3), 13, 0, (
1, 1, 1,
1, 5, 1,
1, 1, 1
)
class SMOOTH_MORE(BuiltinFilter):
name = "Smooth More"
filterargs = (5, 5), 100, 0, (
1, 1, 1, 1, 1,
1, 5, 5, 5, 1,
1, 5, 44, 5, 1,
1, 5, 5, 5, 1,
1, 1, 1, 1, 1
)
class Color3DLUT(MultibandFilter):
"""Three-dimensional color lookup table.
Transforms 3-channel pixels using the values of the channels as coordinates
in the 3D lookup table and interpolating the nearest elements.
This method allows you to apply almost any color transformation
in constant time by using pre-calculated decimated tables.
.. versionadded:: 5.2.0
:param size: Size of the table. One int or tuple of (int, int, int).
Minimal size in any dimension is 2, maximum is 65.
:param table: Flat lookup table. A list of ``channels * size**3``
float elements or a list of ``size**3`` channels-sized
tuples with floats. Channels are changed first,
then first dimension, then second, then third.
Value 0.0 corresponds lowest value of output, 1.0 highest.
:param channels: Number of channels in the table. Could be 3 or 4.
Default is 3.
:param target_mode: A mode for the result image. Should have not less
than ``channels`` channels. Default is ``None``,
which means that mode wouldn't be changed.
"""
name = "Color 3D LUT"
def __init__(self, size, table, channels=3, target_mode=None, **kwargs):
if channels not in (3, 4):
raise ValueError("Only 3 or 4 output channels are supported")
self.size = size = self._check_size(size)
self.channels = channels
self.mode = target_mode
# Hidden flag `_copy_table=False` could be used to avoid extra copying
# of the table if the table is specially made for the constructor.
copy_table = kwargs.get('_copy_table', True)
items = size[0] * size[1] * size[2]
wrong_size = False
if numpy and isinstance(table, numpy.ndarray):
if copy_table:
table = table.copy()
if table.shape in [(items * channels,), (items, channels),
(size[2], size[1], size[0], channels)]:
table = table.reshape(items * channels)
else:
wrong_size = True
else:
if copy_table:
table = list(table)
# Convert to a flat list
if table and isinstance(table[0], (list, tuple)):
table, raw_table = [], table
for pixel in raw_table:
if len(pixel) != channels:
raise ValueError(
"The elements of the table should "
"have a length of {}.".format(channels))
table.extend(pixel)
if wrong_size or len(table) != items * channels:
raise ValueError(
"The table should have either channels * size**3 float items "
"or size**3 items of channels-sized tuples with floats. "
"Table should be: {}x{}x{}x{}. Actual length: {}".format(
channels, size[0], size[1], size[2], len(table)))
self.table = table
@staticmethod
def _check_size(size):
try:
_, _, _ = size
except ValueError:
raise ValueError("Size should be either an integer or "
"a tuple of three integers.")
except TypeError:
size = (size, size, size)
size = [int(x) for x in size]
for size1D in size:
if not 2 <= size1D <= 65:
raise ValueError("Size should be in [2, 65] range.")
return size
@classmethod
def generate(cls, size, callback, channels=3, target_mode=None):
"""Generates new LUT using provided callback.
:param size: Size of the table. Passed to the constructor.
:param callback: Function with three parameters which correspond
three color channels. Will be called ``size**3``
times with values from 0.0 to 1.0 and should return
a tuple with ``channels`` elements.
:param channels: The number of channels which should return callback.
:param target_mode: Passed to the constructor of the resulting
lookup table.
"""
size1D, size2D, size3D = cls._check_size(size)
if channels not in (3, 4):
raise ValueError("Only 3 or 4 output channels are supported")
table = [0] * (size1D * size2D * size3D * channels)
idx_out = 0
for b in range(size3D):
for g in range(size2D):
for r in range(size1D):
table[idx_out:idx_out + channels] = callback(
r / (size1D-1), g / (size2D-1), b / (size3D-1))
idx_out += channels
return cls((size1D, size2D, size3D), table, channels=channels,
target_mode=target_mode, _copy_table=False)
def transform(self, callback, with_normals=False, channels=None,
target_mode=None):
"""Transforms the table values using provided callback and returns
a new LUT with altered values.
:param callback: A function which takes old lookup table values
and returns a new set of values. The number
of arguments which function should take is
``self.channels`` or ``3 + self.channels``
if ``with_normals`` flag is set.
Should return a tuple of ``self.channels`` or
``channels`` elements if it is set.
:param with_normals: If true, ``callback`` will be called with
coordinates in the color cube as the first
three arguments. Otherwise, ``callback``
will be called only with actual color values.
:param channels: The number of channels in the resulting lookup table.
:param target_mode: Passed to the constructor of the resulting
lookup table.
"""
if channels not in (None, 3, 4):
raise ValueError("Only 3 or 4 output channels are supported")
ch_in = self.channels
ch_out = channels or ch_in
size1D, size2D, size3D = self.size
table = [0] * (size1D * size2D * size3D * ch_out)
idx_in = 0
idx_out = 0
for b in range(size3D):
for g in range(size2D):
for r in range(size1D):
values = self.table[idx_in:idx_in + ch_in]
if with_normals:
values = callback(r / (size1D-1), g / (size2D-1),
b / (size3D-1), *values)
else:
values = callback(*values)
table[idx_out:idx_out + ch_out] = values
idx_in += ch_in
idx_out += ch_out
return type(self)(self.size, table, channels=ch_out,
target_mode=target_mode or self.mode,
_copy_table=False)
def __repr__(self):
r = [
"{} from {}".format(self.__class__.__name__,
self.table.__class__.__name__),
"size={:d}x{:d}x{:d}".format(*self.size),
"channels={:d}".format(self.channels),
]
if self.mode:
r.append("target_mode={}".format(self.mode))
return "<{}>".format(" ".join(r))
def filter(self, image):
from . import Image
return image.color_lut_3d(
self.mode or image.mode, Image.LINEAR, self.channels,
self.size[0], self.size[1], self.size[2], self.table)

View File

@ -0,0 +1,480 @@
#
# The Python Imaging Library.
# $Id$
#
# PIL raster font management
#
# History:
# 1996-08-07 fl created (experimental)
# 1997-08-25 fl minor adjustments to handle fonts from pilfont 0.3
# 1999-02-06 fl rewrote most font management stuff in C
# 1999-03-17 fl take pth files into account in load_path (from Richard Jones)
# 2001-02-17 fl added freetype support
# 2001-05-09 fl added TransposedFont wrapper class
# 2002-03-04 fl make sure we have a "L" or "1" font
# 2002-12-04 fl skip non-directory entries in the system path
# 2003-04-29 fl add embedded default font
# 2003-09-27 fl added support for truetype charmap encodings
#
# Todo:
# Adapt to PILFONT2 format (16-bit fonts, compressed, single file)
#
# Copyright (c) 1997-2003 by Secret Labs AB
# Copyright (c) 1996-2003 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
from . import Image
from ._util import isDirectory, isPath, py3
import os
import sys
LAYOUT_BASIC = 0
LAYOUT_RAQM = 1
class _imagingft_not_installed(object):
# module placeholder
def __getattr__(self, id):
raise ImportError("The _imagingft C module is not installed")
try:
from . import _imagingft as core
except ImportError:
core = _imagingft_not_installed()
# FIXME: add support for pilfont2 format (see FontFile.py)
# --------------------------------------------------------------------
# Font metrics format:
# "PILfont" LF
# fontdescriptor LF
# (optional) key=value... LF
# "DATA" LF
# binary data: 256*10*2 bytes (dx, dy, dstbox, srcbox)
#
# To place a character, cut out srcbox and paste at dstbox,
# relative to the character position. Then move the character
# position according to dx, dy.
# --------------------------------------------------------------------
class ImageFont(object):
"PIL font wrapper"
def _load_pilfont(self, filename):
with open(filename, "rb") as fp:
for ext in (".png", ".gif", ".pbm"):
try:
fullname = os.path.splitext(filename)[0] + ext
image = Image.open(fullname)
except Exception:
pass
else:
if image and image.mode in ("1", "L"):
break
else:
raise IOError("cannot find glyph data file")
self.file = fullname
return self._load_pilfont_data(fp, image)
def _load_pilfont_data(self, file, image):
# read PILfont header
if file.readline() != b"PILfont\n":
raise SyntaxError("Not a PILfont file")
file.readline().split(b";")
self.info = [] # FIXME: should be a dictionary
while True:
s = file.readline()
if not s or s == b"DATA\n":
break
self.info.append(s)
# read PILfont metrics
data = file.read(256*20)
# check image
if image.mode not in ("1", "L"):
raise TypeError("invalid font image mode")
image.load()
self.font = Image.core.font(image.im, data)
def getsize(self, text, *args, **kwargs):
return self.font.getsize(text)
def getmask(self, text, mode="", *args, **kwargs):
return self.font.getmask(text, mode)
##
# Wrapper for FreeType fonts. Application code should use the
# <b>truetype</b> factory function to create font objects.
class FreeTypeFont(object):
"FreeType font wrapper (requires _imagingft service)"
def __init__(self, font=None, size=10, index=0, encoding="",
layout_engine=None):
# FIXME: use service provider instead
self.path = font
self.size = size
self.index = index
self.encoding = encoding
if layout_engine not in (LAYOUT_BASIC, LAYOUT_RAQM):
layout_engine = LAYOUT_BASIC
if core.HAVE_RAQM:
layout_engine = LAYOUT_RAQM
if layout_engine == LAYOUT_RAQM and not core.HAVE_RAQM:
layout_engine = LAYOUT_BASIC
self.layout_engine = layout_engine
if isPath(font):
self.font = core.getfont(font, size, index, encoding,
layout_engine=layout_engine)
else:
self.font_bytes = font.read()
self.font = core.getfont(
"", size, index, encoding, self.font_bytes, layout_engine)
def _multiline_split(self, text):
split_character = "\n" if isinstance(text, str) else b"\n"
return text.split(split_character)
def getname(self):
return self.font.family, self.font.style
def getmetrics(self):
return self.font.ascent, self.font.descent
def getsize(self, text, direction=None, features=None):
size, offset = self.font.getsize(text, direction, features)
return (size[0] + offset[0], size[1] + offset[1])
def getsize_multiline(self, text, direction=None,
spacing=4, features=None):
max_width = 0
lines = self._multiline_split(text)
line_spacing = self.getsize('A')[1] + spacing
for line in lines:
line_width, line_height = self.getsize(line, direction, features)
max_width = max(max_width, line_width)
return max_width, len(lines)*line_spacing - spacing
def getoffset(self, text):
return self.font.getsize(text)[1]
def getmask(self, text, mode="", direction=None, features=None):
return self.getmask2(text, mode, direction=direction,
features=features)[0]
def getmask2(self, text, mode="", fill=Image.core.fill, direction=None,
features=None, *args, **kwargs):
size, offset = self.font.getsize(text, direction, features)
im = fill("L", size, 0)
self.font.render(text, im.id, mode == "1", direction, features)
return im, offset
def font_variant(self, font=None, size=None, index=None, encoding=None,
layout_engine=None):
"""
Create a copy of this FreeTypeFont object,
using any specified arguments to override the settings.
Parameters are identical to the parameters used to initialize this
object.
:return: A FreeTypeFont object.
"""
return FreeTypeFont(
font=self.path if font is None else font,
size=self.size if size is None else size,
index=self.index if index is None else index,
encoding=self.encoding if encoding is None else encoding,
layout_engine=layout_engine or self.layout_engine
)
class TransposedFont(object):
"Wrapper for writing rotated or mirrored text"
def __init__(self, font, orientation=None):
"""
Wrapper that creates a transposed font from any existing font
object.
:param font: A font object.
:param orientation: An optional orientation. If given, this should
be one of Image.FLIP_LEFT_RIGHT, Image.FLIP_TOP_BOTTOM,
Image.ROTATE_90, Image.ROTATE_180, or Image.ROTATE_270.
"""
self.font = font
self.orientation = orientation # any 'transpose' argument, or None
def getsize(self, text, *args, **kwargs):
w, h = self.font.getsize(text)
if self.orientation in (Image.ROTATE_90, Image.ROTATE_270):
return h, w
return w, h
def getmask(self, text, mode="", *args, **kwargs):
im = self.font.getmask(text, mode, *args, **kwargs)
if self.orientation is not None:
return im.transpose(self.orientation)
return im
def load(filename):
"""
Load a font file. This function loads a font object from the given
bitmap font file, and returns the corresponding font object.
:param filename: Name of font file.
:return: A font object.
:exception IOError: If the file could not be read.
"""
f = ImageFont()
f._load_pilfont(filename)
return f
def truetype(font=None, size=10, index=0, encoding="",
layout_engine=None):
"""
Load a TrueType or OpenType font from a file or file-like object,
and create a font object.
This function loads a font object from the given file or file-like
object, and creates a font object for a font of the given size.
This function requires the _imagingft service.
:param font: A filename or file-like object containing a TrueType font.
Under Windows, if the file is not found in this filename,
the loader also looks in Windows :file:`fonts/` directory.
:param size: The requested size, in points.
:param index: Which font face to load (default is first available face).
:param encoding: Which font encoding to use (default is Unicode). Common
encodings are "unic" (Unicode), "symb" (Microsoft
Symbol), "ADOB" (Adobe Standard), "ADBE" (Adobe Expert),
and "armn" (Apple Roman). See the FreeType documentation
for more information.
:param layout_engine: Which layout engine to use, if available:
`ImageFont.LAYOUT_BASIC` or `ImageFont.LAYOUT_RAQM`.
:return: A font object.
:exception IOError: If the file could not be read.
"""
try:
return FreeTypeFont(font, size, index, encoding, layout_engine)
except IOError:
ttf_filename = os.path.basename(font)
dirs = []
if sys.platform == "win32":
# check the windows font repository
# NOTE: must use uppercase WINDIR, to work around bugs in
# 1.5.2's os.environ.get()
windir = os.environ.get("WINDIR")
if windir:
dirs.append(os.path.join(windir, "fonts"))
elif sys.platform in ('linux', 'linux2'):
lindirs = os.environ.get("XDG_DATA_DIRS", "")
if not lindirs:
# According to the freedesktop spec, XDG_DATA_DIRS should
# default to /usr/share
lindirs = '/usr/share'
dirs += [os.path.join(lindir, "fonts")
for lindir in lindirs.split(":")]
elif sys.platform == 'darwin':
dirs += ['/Library/Fonts', '/System/Library/Fonts',
os.path.expanduser('~/Library/Fonts')]
ext = os.path.splitext(ttf_filename)[1]
first_font_with_a_different_extension = None
for directory in dirs:
for walkroot, walkdir, walkfilenames in os.walk(directory):
for walkfilename in walkfilenames:
if ext and walkfilename == ttf_filename:
fontpath = os.path.join(walkroot, walkfilename)
return FreeTypeFont(fontpath, size, index,
encoding, layout_engine)
elif (not ext and
os.path.splitext(walkfilename)[0] == ttf_filename):
fontpath = os.path.join(walkroot, walkfilename)
if os.path.splitext(fontpath)[1] == '.ttf':
return FreeTypeFont(fontpath, size, index,
encoding, layout_engine)
if not ext \
and first_font_with_a_different_extension is None:
first_font_with_a_different_extension = fontpath
if first_font_with_a_different_extension:
return FreeTypeFont(first_font_with_a_different_extension, size,
index, encoding, layout_engine)
raise
def load_path(filename):
"""
Load font file. Same as :py:func:`~PIL.ImageFont.load`, but searches for a
bitmap font along the Python path.
:param filename: Name of font file.
:return: A font object.
:exception IOError: If the file could not be read.
"""
for directory in sys.path:
if isDirectory(directory):
if not isinstance(filename, str):
if py3:
filename = filename.decode("utf-8")
else:
filename = filename.encode("utf-8")
try:
return load(os.path.join(directory, filename))
except IOError:
pass
raise IOError("cannot find font file")
def load_default():
"""Load a "better than nothing" default font.
.. versionadded:: 1.1.4
:return: A font object.
"""
from io import BytesIO
import base64
f = ImageFont()
f._load_pilfont_data(
# courB08
BytesIO(base64.b64decode(b'''
UElMZm9udAo7Ozs7OzsxMDsKREFUQQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAA//8AAQAAAAAAAAABAAEA
BgAAAAH/+gADAAAAAQAAAAMABgAGAAAAAf/6AAT//QADAAAABgADAAYAAAAA//kABQABAAYAAAAL
AAgABgAAAAD/+AAFAAEACwAAABAACQAGAAAAAP/5AAUAAAAQAAAAFQAHAAYAAP////oABQAAABUA
AAAbAAYABgAAAAH/+QAE//wAGwAAAB4AAwAGAAAAAf/5AAQAAQAeAAAAIQAIAAYAAAAB//kABAAB
ACEAAAAkAAgABgAAAAD/+QAE//0AJAAAACgABAAGAAAAAP/6AAX//wAoAAAALQAFAAYAAAAB//8A
BAACAC0AAAAwAAMABgAAAAD//AAF//0AMAAAADUAAQAGAAAAAf//AAMAAAA1AAAANwABAAYAAAAB
//kABQABADcAAAA7AAgABgAAAAD/+QAFAAAAOwAAAEAABwAGAAAAAP/5AAYAAABAAAAARgAHAAYA
AAAA//kABQAAAEYAAABLAAcABgAAAAD/+QAFAAAASwAAAFAABwAGAAAAAP/5AAYAAABQAAAAVgAH
AAYAAAAA//kABQAAAFYAAABbAAcABgAAAAD/+QAFAAAAWwAAAGAABwAGAAAAAP/5AAUAAABgAAAA
ZQAHAAYAAAAA//kABQAAAGUAAABqAAcABgAAAAD/+QAFAAAAagAAAG8ABwAGAAAAAf/8AAMAAABv
AAAAcQAEAAYAAAAA//wAAwACAHEAAAB0AAYABgAAAAD/+gAE//8AdAAAAHgABQAGAAAAAP/7AAT/
/gB4AAAAfAADAAYAAAAB//oABf//AHwAAACAAAUABgAAAAD/+gAFAAAAgAAAAIUABgAGAAAAAP/5
AAYAAQCFAAAAiwAIAAYAAP////oABgAAAIsAAACSAAYABgAA////+gAFAAAAkgAAAJgABgAGAAAA
AP/6AAUAAACYAAAAnQAGAAYAAP////oABQAAAJ0AAACjAAYABgAA////+gAFAAAAowAAAKkABgAG
AAD////6AAUAAACpAAAArwAGAAYAAAAA//oABQAAAK8AAAC0AAYABgAA////+gAGAAAAtAAAALsA
BgAGAAAAAP/6AAQAAAC7AAAAvwAGAAYAAP////oABQAAAL8AAADFAAYABgAA////+gAGAAAAxQAA
AMwABgAGAAD////6AAUAAADMAAAA0gAGAAYAAP////oABQAAANIAAADYAAYABgAA////+gAGAAAA
2AAAAN8ABgAGAAAAAP/6AAUAAADfAAAA5AAGAAYAAP////oABQAAAOQAAADqAAYABgAAAAD/+gAF
AAEA6gAAAO8ABwAGAAD////6AAYAAADvAAAA9gAGAAYAAAAA//oABQAAAPYAAAD7AAYABgAA////
+gAFAAAA+wAAAQEABgAGAAD////6AAYAAAEBAAABCAAGAAYAAP////oABgAAAQgAAAEPAAYABgAA
////+gAGAAABDwAAARYABgAGAAAAAP/6AAYAAAEWAAABHAAGAAYAAP////oABgAAARwAAAEjAAYA
BgAAAAD/+gAFAAABIwAAASgABgAGAAAAAf/5AAQAAQEoAAABKwAIAAYAAAAA//kABAABASsAAAEv
AAgABgAAAAH/+QAEAAEBLwAAATIACAAGAAAAAP/5AAX//AEyAAABNwADAAYAAAAAAAEABgACATcA
AAE9AAEABgAAAAH/+QAE//wBPQAAAUAAAwAGAAAAAP/7AAYAAAFAAAABRgAFAAYAAP////kABQAA
AUYAAAFMAAcABgAAAAD/+wAFAAABTAAAAVEABQAGAAAAAP/5AAYAAAFRAAABVwAHAAYAAAAA//sA
BQAAAVcAAAFcAAUABgAAAAD/+QAFAAABXAAAAWEABwAGAAAAAP/7AAYAAgFhAAABZwAHAAYAAP//
//kABQAAAWcAAAFtAAcABgAAAAD/+QAGAAABbQAAAXMABwAGAAAAAP/5AAQAAgFzAAABdwAJAAYA
AP////kABgAAAXcAAAF+AAcABgAAAAD/+QAGAAABfgAAAYQABwAGAAD////7AAUAAAGEAAABigAF
AAYAAP////sABQAAAYoAAAGQAAUABgAAAAD/+wAFAAABkAAAAZUABQAGAAD////7AAUAAgGVAAAB
mwAHAAYAAAAA//sABgACAZsAAAGhAAcABgAAAAD/+wAGAAABoQAAAacABQAGAAAAAP/7AAYAAAGn
AAABrQAFAAYAAAAA//kABgAAAa0AAAGzAAcABgAA////+wAGAAABswAAAboABQAGAAD////7AAUA
AAG6AAABwAAFAAYAAP////sABgAAAcAAAAHHAAUABgAAAAD/+wAGAAABxwAAAc0ABQAGAAD////7
AAYAAgHNAAAB1AAHAAYAAAAA//sABQAAAdQAAAHZAAUABgAAAAH/+QAFAAEB2QAAAd0ACAAGAAAA
Av/6AAMAAQHdAAAB3gAHAAYAAAAA//kABAABAd4AAAHiAAgABgAAAAD/+wAF//0B4gAAAecAAgAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAB
//sAAwACAecAAAHpAAcABgAAAAD/+QAFAAEB6QAAAe4ACAAGAAAAAP/5AAYAAAHuAAAB9AAHAAYA
AAAA//oABf//AfQAAAH5AAUABgAAAAD/+QAGAAAB+QAAAf8ABwAGAAAAAv/5AAMAAgH/AAACAAAJ
AAYAAAAA//kABQABAgAAAAIFAAgABgAAAAH/+gAE//sCBQAAAggAAQAGAAAAAP/5AAYAAAIIAAAC
DgAHAAYAAAAB//kABf/+Ag4AAAISAAUABgAA////+wAGAAACEgAAAhkABQAGAAAAAP/7AAX//gIZ
AAACHgADAAYAAAAA//wABf/9Ah4AAAIjAAEABgAAAAD/+QAHAAACIwAAAioABwAGAAAAAP/6AAT/
+wIqAAACLgABAAYAAAAA//kABP/8Ai4AAAIyAAMABgAAAAD/+gAFAAACMgAAAjcABgAGAAAAAf/5
AAT//QI3AAACOgAEAAYAAAAB//kABP/9AjoAAAI9AAQABgAAAAL/+QAE//sCPQAAAj8AAgAGAAD/
///7AAYAAgI/AAACRgAHAAYAAAAA//kABgABAkYAAAJMAAgABgAAAAH//AAD//0CTAAAAk4AAQAG
AAAAAf//AAQAAgJOAAACUQADAAYAAAAB//kABP/9AlEAAAJUAAQABgAAAAH/+QAF//4CVAAAAlgA
BQAGAAD////7AAYAAAJYAAACXwAFAAYAAP////kABgAAAl8AAAJmAAcABgAA////+QAGAAACZgAA
Am0ABwAGAAD////5AAYAAAJtAAACdAAHAAYAAAAA//sABQACAnQAAAJ5AAcABgAA////9wAGAAAC
eQAAAoAACQAGAAD////3AAYAAAKAAAAChwAJAAYAAP////cABgAAAocAAAKOAAkABgAA////9wAG
AAACjgAAApUACQAGAAD////4AAYAAAKVAAACnAAIAAYAAP////cABgAAApwAAAKjAAkABgAA////
+gAGAAACowAAAqoABgAGAAAAAP/6AAUAAgKqAAACrwAIAAYAAP////cABQAAAq8AAAK1AAkABgAA
////9wAFAAACtQAAArsACQAGAAD////3AAUAAAK7AAACwQAJAAYAAP////gABQAAAsEAAALHAAgA
BgAAAAD/9wAEAAACxwAAAssACQAGAAAAAP/3AAQAAALLAAACzwAJAAYAAAAA//cABAAAAs8AAALT
AAkABgAAAAD/+AAEAAAC0wAAAtcACAAGAAD////6AAUAAALXAAAC3QAGAAYAAP////cABgAAAt0A
AALkAAkABgAAAAD/9wAFAAAC5AAAAukACQAGAAAAAP/3AAUAAALpAAAC7gAJAAYAAAAA//cABQAA
Au4AAALzAAkABgAAAAD/9wAFAAAC8wAAAvgACQAGAAAAAP/4AAUAAAL4AAAC/QAIAAYAAAAA//oA
Bf//Av0AAAMCAAUABgAA////+gAGAAADAgAAAwkABgAGAAD////3AAYAAAMJAAADEAAJAAYAAP//
//cABgAAAxAAAAMXAAkABgAA////9wAGAAADFwAAAx4ACQAGAAD////4AAYAAAAAAAoABwASAAYA
AP////cABgAAAAcACgAOABMABgAA////+gAFAAAADgAKABQAEAAGAAD////6AAYAAAAUAAoAGwAQ
AAYAAAAA//gABgAAABsACgAhABIABgAAAAD/+AAGAAAAIQAKACcAEgAGAAAAAP/4AAYAAAAnAAoA
LQASAAYAAAAA//gABgAAAC0ACgAzABIABgAAAAD/+QAGAAAAMwAKADkAEQAGAAAAAP/3AAYAAAA5
AAoAPwATAAYAAP////sABQAAAD8ACgBFAA8ABgAAAAD/+wAFAAIARQAKAEoAEQAGAAAAAP/4AAUA
AABKAAoATwASAAYAAAAA//gABQAAAE8ACgBUABIABgAAAAD/+AAFAAAAVAAKAFkAEgAGAAAAAP/5
AAUAAABZAAoAXgARAAYAAAAA//gABgAAAF4ACgBkABIABgAAAAD/+AAGAAAAZAAKAGoAEgAGAAAA
AP/4AAYAAABqAAoAcAASAAYAAAAA//kABgAAAHAACgB2ABEABgAAAAD/+AAFAAAAdgAKAHsAEgAG
AAD////4AAYAAAB7AAoAggASAAYAAAAA//gABQAAAIIACgCHABIABgAAAAD/+AAFAAAAhwAKAIwA
EgAGAAAAAP/4AAUAAACMAAoAkQASAAYAAAAA//gABQAAAJEACgCWABIABgAAAAD/+QAFAAAAlgAK
AJsAEQAGAAAAAP/6AAX//wCbAAoAoAAPAAYAAAAA//oABQABAKAACgClABEABgAA////+AAGAAAA
pQAKAKwAEgAGAAD////4AAYAAACsAAoAswASAAYAAP////gABgAAALMACgC6ABIABgAA////+QAG
AAAAugAKAMEAEQAGAAD////4AAYAAgDBAAoAyAAUAAYAAP////kABQACAMgACgDOABMABgAA////
+QAGAAIAzgAKANUAEw==
''')), Image.open(BytesIO(base64.b64decode(b'''
iVBORw0KGgoAAAANSUhEUgAAAx4AAAAUAQAAAAArMtZoAAAEwElEQVR4nABlAJr/AHVE4czCI/4u
Mc4b7vuds/xzjz5/3/7u/n9vMe7vnfH/9++vPn/xyf5zhxzjt8GHw8+2d83u8x27199/nxuQ6Od9
M43/5z2I+9n9ZtmDBwMQECDRQw/eQIQohJXxpBCNVE6QCCAAAAD//wBlAJr/AgALyj1t/wINwq0g
LeNZUworuN1cjTPIzrTX6ofHWeo3v336qPzfEwRmBnHTtf95/fglZK5N0PDgfRTslpGBvz7LFc4F
IUXBWQGjQ5MGCx34EDFPwXiY4YbYxavpnhHFrk14CDAAAAD//wBlAJr/AgKqRooH2gAgPeggvUAA
Bu2WfgPoAwzRAABAAAAAAACQgLz/3Uv4Gv+gX7BJgDeeGP6AAAD1NMDzKHD7ANWr3loYbxsAD791
NAADfcoIDyP44K/jv4Y63/Z+t98Ovt+ub4T48LAAAAD//wBlAJr/AuplMlADJAAAAGuAphWpqhMx
in0A/fRvAYBABPgBwBUgABBQ/sYAyv9g0bCHgOLoGAAAAAAAREAAwI7nr0ArYpow7aX8//9LaP/9
SjdavWA8ePHeBIKB//81/83ndznOaXx379wAAAD//wBlAJr/AqDxW+D3AABAAbUh/QMnbQag/gAY
AYDAAACgtgD/gOqAAAB5IA/8AAAk+n9w0AAA8AAAmFRJuPo27ciC0cD5oeW4E7KA/wD3ECMAn2tt
y8PgwH8AfAxFzC0JzeAMtratAsC/ffwAAAD//wBlAJr/BGKAyCAA4AAAAvgeYTAwHd1kmQF5chkG
ABoMIHcL5xVpTfQbUqzlAAAErwAQBgAAEOClA5D9il08AEh/tUzdCBsXkbgACED+woQg8Si9VeqY
lODCn7lmF6NhnAEYgAAA/NMIAAAAAAD//2JgjLZgVGBg5Pv/Tvpc8hwGBjYGJADjHDrAwPzAjv/H
/Wf3PzCwtzcwHmBgYGcwbZz8wHaCAQMDOwMDQ8MCBgYOC3W7mp+f0w+wHOYxO3OG+e376hsMZjk3
AAAAAP//YmCMY2A4wMAIN5e5gQETPD6AZisDAwMDgzSDAAPjByiHcQMDAwMDg1nOze1lByRu5/47
c4859311AYNZzg0AAAAA//9iYGDBYihOIIMuwIjGL39/fwffA8b//xv/P2BPtzzHwCBjUQAAAAD/
/yLFBrIBAAAA//9i1HhcwdhizX7u8NZNzyLbvT97bfrMf/QHI8evOwcSqGUJAAAA//9iYBB81iSw
pEE170Qrg5MIYydHqwdDQRMrAwcVrQAAAAD//2J4x7j9AAMDn8Q/BgYLBoaiAwwMjPdvMDBYM1Tv
oJodAAAAAP//Yqo/83+dxePWlxl3npsel9lvLfPcqlE9725C+acfVLMEAAAA//9i+s9gwCoaaGMR
evta/58PTEWzr21hufPjA8N+qlnBwAAAAAD//2JiWLci5v1+HmFXDqcnULE/MxgYGBj+f6CaJQAA
AAD//2Ji2FrkY3iYpYC5qDeGgeEMAwPDvwQBBoYvcTwOVLMEAAAA//9isDBgkP///0EOg9z35v//
Gc/eeW7BwPj5+QGZhANUswMAAAD//2JgqGBgYGBgqEMXlvhMPUsAAAAA//8iYDd1AAAAAP//AwDR
w7IkEbzhVQAAAABJRU5ErkJggg==
'''))))
return f

View File

@ -0,0 +1,81 @@
#
# The Python Imaging Library
# $Id$
#
# screen grabber (macOS and Windows only)
#
# History:
# 2001-04-26 fl created
# 2001-09-17 fl use builtin driver, if present
# 2002-11-19 fl added grabclipboard support
#
# Copyright (c) 2001-2002 by Secret Labs AB
# Copyright (c) 2001-2002 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
from . import Image
import sys
if sys.platform not in ["win32", "darwin"]:
raise ImportError("ImageGrab is macOS and Windows only")
if sys.platform == "win32":
grabber = Image.core.grabscreen
elif sys.platform == "darwin":
import os
import tempfile
import subprocess
def grab(bbox=None):
if sys.platform == "darwin":
fh, filepath = tempfile.mkstemp('.png')
os.close(fh)
subprocess.call(['screencapture', '-x', filepath])
im = Image.open(filepath)
im.load()
os.unlink(filepath)
else:
size, data = grabber()
im = Image.frombytes(
"RGB", size, data,
# RGB, 32-bit line padding, origin lower left corner
"raw", "BGR", (size[0]*3 + 3) & -4, -1
)
if bbox:
im = im.crop(bbox)
return im
def grabclipboard():
if sys.platform == "darwin":
fh, filepath = tempfile.mkstemp('.jpg')
os.close(fh)
commands = [
"set theFile to (open for access POSIX file \""
+ filepath + "\" with write permission)",
"try",
" write (the clipboard as JPEG picture) to theFile",
"end try",
"close access theFile"
]
script = ["osascript"]
for command in commands:
script += ["-e", command]
subprocess.call(script)
im = None
if os.stat(filepath).st_size != 0:
im = Image.open(filepath)
im.load()
os.unlink(filepath)
return im
else:
data = Image.core.grabclipboard()
if isinstance(data, bytes):
from . import BmpImagePlugin
import io
return BmpImagePlugin.DibImageFile(io.BytesIO(data))
return data

View File

@ -0,0 +1,271 @@
#
# The Python Imaging Library
# $Id$
#
# a simple math add-on for the Python Imaging Library
#
# History:
# 1999-02-15 fl Original PIL Plus release
# 2005-05-05 fl Simplified and cleaned up for PIL 1.1.6
# 2005-09-12 fl Fixed int() and float() for Python 2.4.1
#
# Copyright (c) 1999-2005 by Secret Labs AB
# Copyright (c) 2005 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
from . import Image, _imagingmath
from ._util import py3
try:
import builtins
except ImportError:
import __builtin__
builtins = __builtin__
VERBOSE = 0
def _isconstant(v):
return isinstance(v, int) or isinstance(v, float)
class _Operand(object):
"""Wraps an image operand, providing standard operators"""
def __init__(self, im):
self.im = im
def __fixup(self, im1):
# convert image to suitable mode
if isinstance(im1, _Operand):
# argument was an image.
if im1.im.mode in ("1", "L"):
return im1.im.convert("I")
elif im1.im.mode in ("I", "F"):
return im1.im
else:
raise ValueError("unsupported mode: %s" % im1.im.mode)
else:
# argument was a constant
if _isconstant(im1) and self.im.mode in ("1", "L", "I"):
return Image.new("I", self.im.size, im1)
else:
return Image.new("F", self.im.size, im1)
def apply(self, op, im1, im2=None, mode=None):
im1 = self.__fixup(im1)
if im2 is None:
# unary operation
out = Image.new(mode or im1.mode, im1.size, None)
im1.load()
try:
op = getattr(_imagingmath, op+"_"+im1.mode)
except AttributeError:
raise TypeError("bad operand type for '%s'" % op)
_imagingmath.unop(op, out.im.id, im1.im.id)
else:
# binary operation
im2 = self.__fixup(im2)
if im1.mode != im2.mode:
# convert both arguments to floating point
if im1.mode != "F":
im1 = im1.convert("F")
if im2.mode != "F":
im2 = im2.convert("F")
if im1.mode != im2.mode:
raise ValueError("mode mismatch")
if im1.size != im2.size:
# crop both arguments to a common size
size = (min(im1.size[0], im2.size[0]),
min(im1.size[1], im2.size[1]))
if im1.size != size:
im1 = im1.crop((0, 0) + size)
if im2.size != size:
im2 = im2.crop((0, 0) + size)
out = Image.new(mode or im1.mode, size, None)
else:
out = Image.new(mode or im1.mode, im1.size, None)
im1.load()
im2.load()
try:
op = getattr(_imagingmath, op+"_"+im1.mode)
except AttributeError:
raise TypeError("bad operand type for '%s'" % op)
_imagingmath.binop(op, out.im.id, im1.im.id, im2.im.id)
return _Operand(out)
# unary operators
def __bool__(self):
# an image is "true" if it contains at least one non-zero pixel
return self.im.getbbox() is not None
if not py3:
# Provide __nonzero__ for pre-Py3k
__nonzero__ = __bool__
del __bool__
def __abs__(self):
return self.apply("abs", self)
def __pos__(self):
return self
def __neg__(self):
return self.apply("neg", self)
# binary operators
def __add__(self, other):
return self.apply("add", self, other)
def __radd__(self, other):
return self.apply("add", other, self)
def __sub__(self, other):
return self.apply("sub", self, other)
def __rsub__(self, other):
return self.apply("sub", other, self)
def __mul__(self, other):
return self.apply("mul", self, other)
def __rmul__(self, other):
return self.apply("mul", other, self)
def __truediv__(self, other):
return self.apply("div", self, other)
def __rtruediv__(self, other):
return self.apply("div", other, self)
def __mod__(self, other):
return self.apply("mod", self, other)
def __rmod__(self, other):
return self.apply("mod", other, self)
def __pow__(self, other):
return self.apply("pow", self, other)
def __rpow__(self, other):
return self.apply("pow", other, self)
if not py3:
# Provide __div__ and __rdiv__ for pre-Py3k
__div__ = __truediv__
__rdiv__ = __rtruediv__
del __truediv__
del __rtruediv__
# bitwise
def __invert__(self):
return self.apply("invert", self)
def __and__(self, other):
return self.apply("and", self, other)
def __rand__(self, other):
return self.apply("and", other, self)
def __or__(self, other):
return self.apply("or", self, other)
def __ror__(self, other):
return self.apply("or", other, self)
def __xor__(self, other):
return self.apply("xor", self, other)
def __rxor__(self, other):
return self.apply("xor", other, self)
def __lshift__(self, other):
return self.apply("lshift", self, other)
def __rshift__(self, other):
return self.apply("rshift", self, other)
# logical
def __eq__(self, other):
return self.apply("eq", self, other)
def __ne__(self, other):
return self.apply("ne", self, other)
def __lt__(self, other):
return self.apply("lt", self, other)
def __le__(self, other):
return self.apply("le", self, other)
def __gt__(self, other):
return self.apply("gt", self, other)
def __ge__(self, other):
return self.apply("ge", self, other)
# conversions
def imagemath_int(self):
return _Operand(self.im.convert("I"))
def imagemath_float(self):
return _Operand(self.im.convert("F"))
# logical
def imagemath_equal(self, other):
return self.apply("eq", self, other, mode="I")
def imagemath_notequal(self, other):
return self.apply("ne", self, other, mode="I")
def imagemath_min(self, other):
return self.apply("min", self, other)
def imagemath_max(self, other):
return self.apply("max", self, other)
def imagemath_convert(self, mode):
return _Operand(self.im.convert(mode))
ops = {}
for k, v in list(globals().items()):
if k[:10] == "imagemath_":
ops[k[10:]] = v
def eval(expression, _dict={}, **kw):
"""
Evaluates an image expression.
:param expression: A string containing a Python-style expression.
:param options: Values to add to the evaluation context. You
can either use a dictionary, or one or more keyword
arguments.
:return: The evaluated expression. This is usually an image object, but can
also be an integer, a floating point value, or a pixel tuple,
depending on the expression.
"""
# build execution namespace
args = ops.copy()
args.update(_dict)
args.update(kw)
for k, v in list(args.items()):
if hasattr(v, "im"):
args[k] = _Operand(v)
out = builtins.eval(expression, args)
try:
return out.im
except AttributeError:
return out

View File

@ -0,0 +1,56 @@
#
# The Python Imaging Library.
# $Id$
#
# standard mode descriptors
#
# History:
# 2006-03-20 fl Added
#
# Copyright (c) 2006 by Secret Labs AB.
# Copyright (c) 2006 by Fredrik Lundh.
#
# See the README file for information on usage and redistribution.
#
# mode descriptor cache
_modes = None
class ModeDescriptor(object):
"""Wrapper for mode strings."""
def __init__(self, mode, bands, basemode, basetype):
self.mode = mode
self.bands = bands
self.basemode = basemode
self.basetype = basetype
def __str__(self):
return self.mode
def getmode(mode):
"""Gets a mode descriptor for the given mode."""
global _modes
if not _modes:
# initialize mode cache
from . import Image
modes = {}
# core modes
for m, (basemode, basetype, bands) in Image._MODEINFO.items():
modes[m] = ModeDescriptor(m, bands, basemode, basetype)
# extra experimental modes
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["PA"] = ModeDescriptor("PA", ("P", "A"), "RGB", "L")
# mapping modes
modes["I;16"] = ModeDescriptor("I;16", "I", "L", "L")
modes["I;16L"] = ModeDescriptor("I;16L", "I", "L", "L")
modes["I;16B"] = ModeDescriptor("I;16B", "I", "L", "L")
# set global mode cache atomically
_modes = modes
return _modes[mode]

View File

@ -0,0 +1,245 @@
# A binary morphology add-on for the Python Imaging Library
#
# History:
# 2014-06-04 Initial version.
#
# Copyright (c) 2014 Dov Grobgeld <dov.grobgeld@gmail.com>
from __future__ import print_function
from . import Image, _imagingmorph
import re
LUT_SIZE = 1 << 9
class LutBuilder(object):
"""A class for building a MorphLut from a descriptive language
The input patterns is a list of a strings sequences like these::
4:(...
.1.
111)->1
(whitespaces including linebreaks are ignored). The option 4
describes a series of symmetry operations (in this case a
4-rotation), the pattern is described by:
- . or X - Ignore
- 1 - Pixel is on
- 0 - Pixel is off
The result of the operation is described after "->" string.
The default is to return the current pixel value, which is
returned if no other match is found.
Operations:
- 4 - 4 way rotation
- N - Negate
- 1 - Dummy op for no other operation (an op must always be given)
- M - Mirroring
Example::
lb = LutBuilder(patterns = ["4:(... .1. 111)->1"])
lut = lb.build_lut()
"""
def __init__(self, patterns=None, op_name=None):
if patterns is not None:
self.patterns = patterns
else:
self.patterns = []
self.lut = None
if op_name is not None:
known_patterns = {
'corner': ['1:(... ... ...)->0',
'4:(00. 01. ...)->1'],
'dilation4': ['4:(... .0. .1.)->1'],
'dilation8': ['4:(... .0. .1.)->1',
'4:(... .0. ..1)->1'],
'erosion4': ['4:(... .1. .0.)->0'],
'erosion8': ['4:(... .1. .0.)->0',
'4:(... .1. ..0)->0'],
'edge': ['1:(... ... ...)->0',
'4:(.0. .1. ...)->1',
'4:(01. .1. ...)->1']
}
if op_name not in known_patterns:
raise Exception('Unknown pattern '+op_name+'!')
self.patterns = known_patterns[op_name]
def add_patterns(self, patterns):
self.patterns += patterns
def build_default_lut(self):
symbols = [0, 1]
m = 1 << 4 # pos of current pixel
self.lut = bytearray(symbols[(i & m) > 0] for i in range(LUT_SIZE))
def get_lut(self):
return self.lut
def _string_permute(self, pattern, permutation):
"""string_permute takes a pattern and a permutation and returns the
string permuted according to the permutation list.
"""
assert(len(permutation) == 9)
return ''.join(pattern[p] for p in permutation)
def _pattern_permute(self, basic_pattern, options, basic_result):
"""pattern_permute takes a basic pattern and its result and clones
the pattern according to the modifications described in the $options
parameter. It returns a list of all cloned patterns."""
patterns = [(basic_pattern, basic_result)]
# rotations
if '4' in options:
res = patterns[-1][1]
for i in range(4):
patterns.append(
(self._string_permute(patterns[-1][0], [6, 3, 0,
7, 4, 1,
8, 5, 2]), res))
# mirror
if 'M' in options:
n = len(patterns)
for pattern, res in patterns[0:n]:
patterns.append(
(self._string_permute(pattern, [2, 1, 0,
5, 4, 3,
8, 7, 6]), res))
# negate
if 'N' in options:
n = len(patterns)
for pattern, res in patterns[0:n]:
# Swap 0 and 1
pattern = (pattern
.replace('0', 'Z')
.replace('1', '0')
.replace('Z', '1'))
res = 1-int(res)
patterns.append((pattern, res))
return patterns
def build_lut(self):
"""Compile all patterns into a morphology lut.
TBD :Build based on (file) morphlut:modify_lut
"""
self.build_default_lut()
patterns = []
# Parse and create symmetries of the patterns strings
for p in self.patterns:
m = re.search(
r'(\w*):?\s*\((.+?)\)\s*->\s*(\d)', p.replace('\n', ''))
if not m:
raise Exception('Syntax error in pattern "'+p+'"')
options = m.group(1)
pattern = m.group(2)
result = int(m.group(3))
# Get rid of spaces
pattern = pattern.replace(' ', '').replace('\n', '')
patterns += self._pattern_permute(pattern, options, result)
# compile the patterns into regular expressions for speed
for i, pattern in enumerate(patterns):
p = pattern[0].replace('.', 'X').replace('X', '[01]')
p = re.compile(p)
patterns[i] = (p, pattern[1])
# Step through table and find patterns that match.
# Note that all the patterns are searched. The last one
# caught overrides
for i in range(LUT_SIZE):
# Build the bit pattern
bitpattern = bin(i)[2:]
bitpattern = ('0'*(9-len(bitpattern)) + bitpattern)[::-1]
for p, r in patterns:
if p.match(bitpattern):
self.lut[i] = [0, 1][r]
return self.lut
class MorphOp(object):
"""A class for binary morphological operators"""
def __init__(self,
lut=None,
op_name=None,
patterns=None):
"""Create a binary morphological operator"""
self.lut = lut
if op_name is not None:
self.lut = LutBuilder(op_name=op_name).build_lut()
elif patterns is not None:
self.lut = LutBuilder(patterns=patterns).build_lut()
def apply(self, image):
"""Run a single morphological operation on an image
Returns a tuple of the number of changed pixels and the
morphed image"""
if self.lut is None:
raise Exception('No operator loaded')
if image.mode != 'L':
raise Exception('Image must be binary, meaning it must use mode L')
outimage = Image.new(image.mode, image.size, None)
count = _imagingmorph.apply(
bytes(self.lut), image.im.id, outimage.im.id)
return count, outimage
def match(self, image):
"""Get a list of coordinates matching the morphological operation on
an image.
Returns a list of tuples of (x,y) coordinates
of all matching pixels. See :ref:`coordinate-system`."""
if self.lut is None:
raise Exception('No operator loaded')
if image.mode != 'L':
raise Exception('Image must be binary, meaning it must use mode L')
return _imagingmorph.match(bytes(self.lut), image.im.id)
def get_on_pixels(self, image):
"""Get a list of all turned on pixels in a binary image
Returns a list of tuples of (x,y) coordinates
of all matching pixels. See :ref:`coordinate-system`."""
if image.mode != 'L':
raise Exception('Image must be binary, meaning it must use mode L')
return _imagingmorph.get_on_pixels(image.im.id)
def load_lut(self, filename):
"""Load an operator from an mrl file"""
with open(filename, 'rb') as f:
self.lut = bytearray(f.read())
if len(self.lut) != LUT_SIZE:
self.lut = None
raise Exception('Wrong size operator file!')
def save_lut(self, filename):
"""Save an operator to an mrl file"""
if self.lut is None:
raise Exception('No operator loaded')
with open(filename, 'wb') as f:
f.write(self.lut)
def set_lut(self, lut):
"""Set the lut from an external source"""
self.lut = lut

View File

@ -0,0 +1,620 @@
#
# The Python Imaging Library.
# $Id$
#
# standard image operations
#
# History:
# 2001-10-20 fl Created
# 2001-10-23 fl Added autocontrast operator
# 2001-12-18 fl Added Kevin's fit operator
# 2004-03-14 fl Fixed potential division by zero in equalize
# 2005-05-05 fl Fixed equalize for low number of values
#
# Copyright (c) 2001-2004 by Secret Labs AB
# Copyright (c) 2001-2004 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
from . import Image
from ._util import isStringType
import operator
import functools
import warnings
#
# helpers
def _border(border):
if isinstance(border, tuple):
if len(border) == 2:
left, top = right, bottom = border
elif len(border) == 4:
left, top, right, bottom = border
else:
left = top = right = bottom = border
return left, top, right, bottom
def _color(color, mode):
if isStringType(color):
from . import ImageColor
color = ImageColor.getcolor(color, mode)
return color
def _lut(image, lut):
if image.mode == "P":
# FIXME: apply to lookup table, not image data
raise NotImplementedError("mode P support coming soon")
elif image.mode in ("L", "RGB"):
if image.mode == "RGB" and len(lut) == 256:
lut = lut + lut + lut
return image.point(lut)
else:
raise IOError("not supported for this image mode")
#
# actions
def autocontrast(image, cutoff=0, ignore=None):
"""
Maximize (normalize) image contrast. This function calculates a
histogram of the input image, removes **cutoff** percent of the
lightest and darkest pixels from the histogram, and remaps the image
so that the darkest pixel becomes black (0), and the lightest
becomes white (255).
:param image: The image to process.
:param cutoff: How many percent to cut off from the histogram.
:param ignore: The background pixel value (use None for no background).
:return: An image.
"""
histogram = image.histogram()
lut = []
for layer in range(0, len(histogram), 256):
h = histogram[layer:layer+256]
if ignore is not None:
# get rid of outliers
try:
h[ignore] = 0
except TypeError:
# assume sequence
for ix in ignore:
h[ix] = 0
if cutoff:
# cut off pixels from both ends of the histogram
# get number of pixels
n = 0
for ix in range(256):
n = n + h[ix]
# remove cutoff% pixels from the low end
cut = n * cutoff // 100
for lo in range(256):
if cut > h[lo]:
cut = cut - h[lo]
h[lo] = 0
else:
h[lo] -= cut
cut = 0
if cut <= 0:
break
# remove cutoff% samples from the hi end
cut = n * cutoff // 100
for hi in range(255, -1, -1):
if cut > h[hi]:
cut = cut - h[hi]
h[hi] = 0
else:
h[hi] -= cut
cut = 0
if cut <= 0:
break
# find lowest/highest samples after preprocessing
for lo in range(256):
if h[lo]:
break
for hi in range(255, -1, -1):
if h[hi]:
break
if hi <= lo:
# don't bother
lut.extend(list(range(256)))
else:
scale = 255.0 / (hi - lo)
offset = -lo * scale
for ix in range(256):
ix = int(ix * scale + offset)
if ix < 0:
ix = 0
elif ix > 255:
ix = 255
lut.append(ix)
return _lut(image, lut)
def colorize(image, black, white, mid=None, blackpoint=0,
whitepoint=255, midpoint=127):
"""
Colorize grayscale image.
This function calculates a color wedge which maps all black pixels in
the source image to the first color and all white pixels to the
second color. If **mid** is specified, it uses three-color mapping.
The **black** and **white** arguments should be RGB tuples or color names;
optionally you can use three-color mapping by also specifying **mid**.
Mapping positions for any of the colors can be specified
(e.g. **blackpoint**), where these parameters are the integer
value corresponding to where the corresponding color should be mapped.
These parameters must have logical order, such that
**blackpoint** <= **midpoint** <= **whitepoint** (if **mid** is specified).
:param image: The image to colorize.
:param black: The color to use for black input pixels.
:param white: The color to use for white input pixels.
:param mid: The color to use for midtone input pixels.
:param blackpoint: an int value [0, 255] for the black mapping.
:param whitepoint: an int value [0, 255] for the white mapping.
:param midpoint: an int value [0, 255] for the midtone mapping.
:return: An image.
"""
# Initial asserts
assert image.mode == "L"
if mid is None:
assert 0 <= blackpoint <= whitepoint <= 255
else:
assert 0 <= blackpoint <= midpoint <= whitepoint <= 255
# Define colors from arguments
black = _color(black, "RGB")
white = _color(white, "RGB")
if mid is not None:
mid = _color(mid, "RGB")
# Empty lists for the mapping
red = []
green = []
blue = []
# Create the low-end values
for i in range(0, blackpoint):
red.append(black[0])
green.append(black[1])
blue.append(black[2])
# Create the mapping (2-color)
if mid is None:
range_map = range(0, whitepoint - blackpoint)
for i in range_map:
red.append(black[0] + i * (white[0] - black[0]) // len(range_map))
green.append(black[1] + i * (white[1] - black[1]) // len(range_map))
blue.append(black[2] + i * (white[2] - black[2]) // len(range_map))
# Create the mapping (3-color)
else:
range_map1 = range(0, midpoint - blackpoint)
range_map2 = range(0, whitepoint - midpoint)
for i in range_map1:
red.append(black[0] + i * (mid[0] - black[0]) // len(range_map1))
green.append(black[1] + i * (mid[1] - black[1]) // len(range_map1))
blue.append(black[2] + i * (mid[2] - black[2]) // len(range_map1))
for i in range_map2:
red.append(mid[0] + i * (white[0] - mid[0]) // len(range_map2))
green.append(mid[1] + i * (white[1] - mid[1]) // len(range_map2))
blue.append(mid[2] + i * (white[2] - mid[2]) // len(range_map2))
# Create the high-end values
for i in range(0, 256 - whitepoint):
red.append(white[0])
green.append(white[1])
blue.append(white[2])
# Return converted image
image = image.convert("RGB")
return _lut(image, red + green + blue)
def pad(image, size, method=Image.NEAREST, color=None, centering=(0.5, 0.5)):
"""
Returns a sized and padded version of the image, expanded to fill the
requested aspect ratio and size.
:param image: The image to size and crop.
:param size: The requested output size in pixels, given as a
(width, height) tuple.
:param method: What resampling method to use. Default is
:py:attr:`PIL.Image.NEAREST`.
:param color: The background color of the padded image.
:param centering: Control the position of the original image within the
padded version.
(0.5, 0.5) will keep the image centered
(0, 0) will keep the image aligned to the top left
(1, 1) will keep the image aligned to the bottom
right
:return: An image.
"""
im_ratio = image.width / image.height
dest_ratio = float(size[0]) / size[1]
if im_ratio == dest_ratio:
out = image.resize(size, resample=method)
else:
out = Image.new(image.mode, size, color)
if im_ratio > dest_ratio:
new_height = int(image.height / image.width * size[0])
if new_height != size[1]:
image = image.resize((size[0], new_height), resample=method)
y = int((size[1] - new_height) * max(0, min(centering[1], 1)))
out.paste(image, (0, y))
else:
new_width = int(image.width / image.height * size[1])
if new_width != size[0]:
image = image.resize((new_width, size[1]), resample=method)
x = int((size[0] - new_width) * max(0, min(centering[0], 1)))
out.paste(image, (x, 0))
return out
def crop(image, border=0):
"""
Remove border from image. The same amount of pixels are removed
from all four sides. This function works on all image modes.
.. seealso:: :py:meth:`~PIL.Image.Image.crop`
:param image: The image to crop.
:param border: The number of pixels to remove.
:return: An image.
"""
left, top, right, bottom = _border(border)
return image.crop(
(left, top, image.size[0]-right, image.size[1]-bottom)
)
def scale(image, factor, resample=Image.NEAREST):
"""
Returns a rescaled image by a specific factor given in parameter.
A factor greater than 1 expands the image, between 0 and 1 contracts the
image.
:param image: The image to rescale.
:param factor: The expansion factor, as a float.
:param resample: An optional resampling filter. Same values possible as
in the PIL.Image.resize function.
:returns: An :py:class:`~PIL.Image.Image` object.
"""
if factor == 1:
return image.copy()
elif factor <= 0:
raise ValueError("the factor must be greater than 0")
else:
size = (int(round(factor * image.width)),
int(round(factor * image.height)))
return image.resize(size, resample)
def deform(image, deformer, resample=Image.BILINEAR):
"""
Deform the image.
:param image: The image to deform.
:param deformer: A deformer object. Any object that implements a
**getmesh** method can be used.
:param resample: An optional resampling filter. Same values possible as
in the PIL.Image.transform function.
:return: An image.
"""
return image.transform(
image.size, Image.MESH, deformer.getmesh(image), resample
)
def equalize(image, mask=None):
"""
Equalize the image histogram. This function applies a non-linear
mapping to the input image, in order to create a uniform
distribution of grayscale values in the output image.
:param image: The image to equalize.
:param mask: An optional mask. If given, only the pixels selected by
the mask are included in the analysis.
:return: An image.
"""
if image.mode == "P":
image = image.convert("RGB")
h = image.histogram(mask)
lut = []
for b in range(0, len(h), 256):
histo = [_f for _f in h[b:b+256] if _f]
if len(histo) <= 1:
lut.extend(list(range(256)))
else:
step = (functools.reduce(operator.add, histo) - histo[-1]) // 255
if not step:
lut.extend(list(range(256)))
else:
n = step // 2
for i in range(256):
lut.append(n // step)
n = n + h[i+b]
return _lut(image, lut)
def expand(image, border=0, fill=0):
"""
Add border to the image
:param image: The image to expand.
:param border: Border width, in pixels.
:param fill: Pixel fill value (a color value). Default is 0 (black).
:return: An image.
"""
left, top, right, bottom = _border(border)
width = left + image.size[0] + right
height = top + image.size[1] + bottom
out = Image.new(image.mode, (width, height), _color(fill, image.mode))
out.paste(image, (left, top))
return out
def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)):
"""
Returns a sized and cropped version of the image, cropped to the
requested aspect ratio and size.
This function was contributed by Kevin Cazabon.
:param image: The image to size and crop.
:param size: The requested output size in pixels, given as a
(width, height) tuple.
:param method: What resampling method to use. Default is
:py:attr:`PIL.Image.NEAREST`.
:param bleed: Remove a border around the outside of the image from all
four edges. The value is a decimal percentage (use 0.01 for
one percent). The default value is 0 (no border).
Cannot be greater than or equal to 0.5.
:param centering: Control the cropping position. Use (0.5, 0.5) for
center cropping (e.g. if cropping the width, take 50% off
of the left side, and therefore 50% off the right side).
(0.0, 0.0) will crop from the top left corner (i.e. if
cropping the width, take all of the crop off of the right
side, and if cropping the height, take all of it off the
bottom). (1.0, 0.0) will crop from the bottom left
corner, etc. (i.e. if cropping the width, take all of the
crop off the left side, and if cropping the height take
none from the top, and therefore all off the bottom).
:return: An image.
"""
# by Kevin Cazabon, Feb 17/2000
# kevin@cazabon.com
# http://www.cazabon.com
# ensure centering is mutable
centering = list(centering)
if not 0.0 <= centering[0] <= 1.0:
centering[0] = 0.5
if not 0.0 <= centering[1] <= 1.0:
centering[1] = 0.5
if not 0.0 <= bleed < 0.5:
bleed = 0.0
# calculate the area to use for resizing and cropping, subtracting
# the 'bleed' around the edges
# number of pixels to trim off on Top and Bottom, Left and Right
bleed_pixels = (bleed * image.size[0], bleed * image.size[1])
live_size = (image.size[0] - bleed_pixels[0] * 2,
image.size[1] - bleed_pixels[1] * 2)
# calculate the aspect ratio of the live_size
live_size_ratio = float(live_size[0]) / live_size[1]
# calculate the aspect ratio of the output image
output_ratio = float(size[0]) / size[1]
# figure out if the sides or top/bottom will be cropped off
if live_size_ratio >= output_ratio:
# live_size is wider than what's needed, crop the sides
crop_width = output_ratio * live_size[1]
crop_height = live_size[1]
else:
# live_size is taller than what's needed, crop the top and bottom
crop_width = live_size[0]
crop_height = live_size[0] / output_ratio
# make the crop
crop_left = bleed_pixels[0] + (live_size[0]-crop_width) * centering[0]
crop_top = bleed_pixels[1] + (live_size[1]-crop_height) * centering[1]
crop = (
crop_left, crop_top,
crop_left + crop_width, crop_top + crop_height
)
# resize the image and return it
return image.resize(size, method, box=crop)
def flip(image):
"""
Flip the image vertically (top to bottom).
:param image: The image to flip.
:return: An image.
"""
return image.transpose(Image.FLIP_TOP_BOTTOM)
def grayscale(image):
"""
Convert the image to grayscale.
:param image: The image to convert.
:return: An image.
"""
return image.convert("L")
def invert(image):
"""
Invert (negate) the image.
:param image: The image to invert.
:return: An image.
"""
lut = []
for i in range(256):
lut.append(255-i)
return _lut(image, lut)
def mirror(image):
"""
Flip image horizontally (left to right).
:param image: The image to mirror.
:return: An image.
"""
return image.transpose(Image.FLIP_LEFT_RIGHT)
def posterize(image, bits):
"""
Reduce the number of bits for each color channel.
:param image: The image to posterize.
:param bits: The number of bits to keep for each channel (1-8).
:return: An image.
"""
lut = []
mask = ~(2**(8-bits)-1)
for i in range(256):
lut.append(i & mask)
return _lut(image, lut)
def solarize(image, threshold=128):
"""
Invert all pixel values above a threshold.
:param image: The image to solarize.
:param threshold: All pixels above this greyscale level are inverted.
:return: An image.
"""
lut = []
for i in range(256):
if i < threshold:
lut.append(i)
else:
lut.append(255-i)
return _lut(image, lut)
# --------------------------------------------------------------------
# PIL USM components, from Kevin Cazabon.
def gaussian_blur(im, radius=None):
""" PIL_usm.gblur(im, [radius])"""
warnings.warn(
'PIL.ImageOps.gaussian_blur is deprecated. '
'Use PIL.ImageFilter.GaussianBlur instead. '
'This function will be removed in a future version.',
DeprecationWarning
)
if radius is None:
radius = 5.0
im.load()
return im.im.gaussian_blur(radius)
def gblur(im, radius=None):
""" PIL_usm.gblur(im, [radius])"""
warnings.warn(
'PIL.ImageOps.gblur is deprecated. '
'Use PIL.ImageFilter.GaussianBlur instead. '
'This function will be removed in a future version.',
DeprecationWarning
)
return gaussian_blur(im, radius)
def unsharp_mask(im, radius=None, percent=None, threshold=None):
""" PIL_usm.usm(im, [radius, percent, threshold])"""
warnings.warn(
'PIL.ImageOps.unsharp_mask is deprecated. '
'Use PIL.ImageFilter.UnsharpMask instead. '
'This function will be removed in a future version.',
DeprecationWarning
)
if radius is None:
radius = 5.0
if percent is None:
percent = 150
if threshold is None:
threshold = 3
im.load()
return im.im.unsharp_mask(radius, percent, threshold)
def usm(im, radius=None, percent=None, threshold=None):
""" PIL_usm.usm(im, [radius, percent, threshold])"""
warnings.warn(
'PIL.ImageOps.usm is deprecated. '
'Use PIL.ImageFilter.UnsharpMask instead. '
'This function will be removed in a future version.',
DeprecationWarning
)
return unsharp_mask(im, radius, percent, threshold)
def box_blur(image, radius):
"""
Blur the image by setting each pixel to the average value of the pixels
in a square box extending radius pixels in each direction.
Supports float radius of arbitrary size. Uses an optimized implementation
which runs in linear time relative to the size of the image
for any radius value.
:param image: The image to blur.
:param radius: Size of the box in one direction. Radius 0 does not blur,
returns an identical image. Radius 1 takes 1 pixel
in each direction, i.e. 9 pixels in total.
:return: An image.
"""
warnings.warn(
'PIL.ImageOps.box_blur is deprecated. '
'Use PIL.ImageFilter.BoxBlur instead. '
'This function will be removed in a future version.',
DeprecationWarning
)
image.load()
return image._new(image.im.box_blur(radius))

View File

@ -0,0 +1,216 @@
#
# The Python Imaging Library.
# $Id$
#
# image palette object
#
# History:
# 1996-03-11 fl Rewritten.
# 1997-01-03 fl Up and running.
# 1997-08-23 fl Added load hack
# 2001-04-16 fl Fixed randint shadow bug in random()
#
# Copyright (c) 1997-2001 by Secret Labs AB
# Copyright (c) 1996-1997 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
import array
from . import ImageColor, GimpPaletteFile, GimpGradientFile, PaletteFile
class ImagePalette(object):
"""
Color palette for palette mapped images
:param mode: The mode to use for the Palette. See:
:ref:`concept-modes`. Defaults to "RGB"
:param palette: An optional palette. If given, it must be a bytearray,
an array or a list of ints between 0-255 and of length ``size``
times the number of colors in ``mode``. The list must be aligned
by channel (All R values must be contiguous in the list before G
and B values.) Defaults to 0 through 255 per channel.
:param size: An optional palette size. If given, it cannot be equal to
or greater than 256. Defaults to 0.
"""
def __init__(self, mode="RGB", palette=None, size=0):
self.mode = mode
self.rawmode = None # if set, palette contains raw data
self.palette = palette or bytearray(range(256))*len(self.mode)
self.colors = {}
self.dirty = None
if ((size == 0 and len(self.mode)*256 != len(self.palette)) or
(size != 0 and size != len(self.palette))):
raise ValueError("wrong palette size")
def copy(self):
new = ImagePalette()
new.mode = self.mode
new.rawmode = self.rawmode
if self.palette is not None:
new.palette = self.palette[:]
new.colors = self.colors.copy()
new.dirty = self.dirty
return new
def getdata(self):
"""
Get palette contents in format suitable for the low-level
``im.putpalette`` primitive.
.. warning:: This method is experimental.
"""
if self.rawmode:
return self.rawmode, self.palette
return self.mode + ";L", self.tobytes()
def tobytes(self):
"""Convert palette to bytes.
.. warning:: This method is experimental.
"""
if self.rawmode:
raise ValueError("palette contains raw palette data")
if isinstance(self.palette, bytes):
return self.palette
arr = array.array("B", self.palette)
if hasattr(arr, 'tobytes'):
return arr.tobytes()
return arr.tostring()
# Declare tostring as an alias for tobytes
tostring = tobytes
def getcolor(self, color):
"""Given an rgb tuple, allocate palette entry.
.. warning:: This method is experimental.
"""
if self.rawmode:
raise ValueError("palette contains raw palette data")
if isinstance(color, tuple):
try:
return self.colors[color]
except KeyError:
# allocate new color slot
if isinstance(self.palette, bytes):
self.palette = bytearray(self.palette)
index = len(self.colors)
if index >= 256:
raise ValueError("cannot allocate more than 256 colors")
self.colors[color] = index
self.palette[index] = color[0]
self.palette[index+256] = color[1]
self.palette[index+512] = color[2]
self.dirty = 1
return index
else:
raise ValueError("unknown color specifier: %r" % color)
def save(self, fp):
"""Save palette to text file.
.. warning:: This method is experimental.
"""
if self.rawmode:
raise ValueError("palette contains raw palette data")
if isinstance(fp, str):
fp = open(fp, "w")
fp.write("# Palette\n")
fp.write("# Mode: %s\n" % self.mode)
for i in range(256):
fp.write("%d" % i)
for j in range(i*len(self.mode), (i+1)*len(self.mode)):
try:
fp.write(" %d" % self.palette[j])
except IndexError:
fp.write(" 0")
fp.write("\n")
fp.close()
# --------------------------------------------------------------------
# Internal
def raw(rawmode, data):
palette = ImagePalette()
palette.rawmode = rawmode
palette.palette = data
palette.dirty = 1
return palette
# --------------------------------------------------------------------
# Factories
def make_linear_lut(black, white):
lut = []
if black == 0:
for i in range(256):
lut.append(white*i//255)
else:
raise NotImplementedError # FIXME
return lut
def make_gamma_lut(exp):
lut = []
for i in range(256):
lut.append(int(((i / 255.0) ** exp) * 255.0 + 0.5))
return lut
def negative(mode="RGB"):
palette = list(range(256))
palette.reverse()
return ImagePalette(mode, palette * len(mode))
def random(mode="RGB"):
from random import randint
palette = []
for i in range(256*len(mode)):
palette.append(randint(0, 255))
return ImagePalette(mode, palette)
def sepia(white="#fff0c0"):
r, g, b = ImageColor.getrgb(white)
r = make_linear_lut(0, r)
g = make_linear_lut(0, g)
b = make_linear_lut(0, b)
return ImagePalette("RGB", r + g + b)
def wedge(mode="RGB"):
return ImagePalette(mode, list(range(256)) * len(mode))
def load(filename):
# FIXME: supports GIMP gradients only
with open(filename, "rb") as fp:
for paletteHandler in [
GimpPaletteFile.GimpPaletteFile,
GimpGradientFile.GimpGradientFile,
PaletteFile.PaletteFile
]:
try:
fp.seek(0)
lut = paletteHandler(fp).getpalette()
if lut:
break
except (SyntaxError, ValueError):
# import traceback
# traceback.print_exc()
pass
else:
raise IOError("cannot load palette")
return lut # data, rawmode

View File

@ -0,0 +1,20 @@
#
# The Python Imaging Library
# $Id$
#
# path interface
#
# History:
# 1996-11-04 fl Created
# 2002-04-14 fl Added documentation stub class
#
# Copyright (c) Secret Labs AB 1997.
# Copyright (c) Fredrik Lundh 1996.
#
# See the README file for information on usage and redistribution.
#
from . import Image
Path = Image.core.path

View File

@ -0,0 +1,218 @@
#
# The Python Imaging Library.
# $Id$
#
# a simple Qt image interface.
#
# history:
# 2006-06-03 fl: created
# 2006-06-04 fl: inherit from QImage instead of wrapping it
# 2006-06-05 fl: removed toimage helper; move string support to ImageQt
# 2013-11-13 fl: add support for Qt5 (aurelien.ballier@cyclonit.com)
#
# Copyright (c) 2006 by Secret Labs AB
# Copyright (c) 2006 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
from . import Image
from ._util import isPath, py3
from io import BytesIO
import sys
qt_versions = [
['5', 'PyQt5'],
['side2', 'PySide2'],
['4', 'PyQt4'],
['side', 'PySide']
]
# If a version has already been imported, attempt it first
qt_versions.sort(key=lambda qt_version: qt_version[1] in sys.modules,
reverse=True)
for qt_version, qt_module in qt_versions:
try:
if qt_module == 'PyQt5':
from PyQt5.QtGui import QImage, qRgba, QPixmap
from PyQt5.QtCore import QBuffer, QIODevice
elif qt_module == 'PySide2':
from PySide2.QtGui import QImage, qRgba, QPixmap
from PySide2.QtCore import QBuffer, QIODevice
elif qt_module == 'PyQt4':
from PyQt4.QtGui import QImage, qRgba, QPixmap
from PyQt4.QtCore import QBuffer, QIODevice
elif qt_module == 'PySide':
from PySide.QtGui import QImage, qRgba, QPixmap
from PySide.QtCore import QBuffer, QIODevice
except (ImportError, RuntimeError):
continue
qt_is_installed = True
break
else:
qt_is_installed = False
qt_version = None
def rgb(r, g, b, a=255):
"""(Internal) Turns an RGB color into a Qt compatible color integer."""
# use qRgb to pack the colors, and then turn the resulting long
# into a negative integer with the same bitpattern.
return (qRgba(r, g, b, a) & 0xffffffff)
def fromqimage(im):
"""
:param im: A PIL Image object, or a file name
(given either as Python string or a PyQt string object)
"""
buffer = QBuffer()
buffer.open(QIODevice.ReadWrite)
# preserve alha channel with png
# otherwise ppm is more friendly with Image.open
if im.hasAlphaChannel():
im.save(buffer, 'png')
else:
im.save(buffer, 'ppm')
b = BytesIO()
try:
b.write(buffer.data())
except TypeError:
# workaround for Python 2
b.write(str(buffer.data()))
buffer.close()
b.seek(0)
return Image.open(b)
def fromqpixmap(im):
return fromqimage(im)
# buffer = QBuffer()
# buffer.open(QIODevice.ReadWrite)
# # im.save(buffer)
# # What if png doesn't support some image features like animation?
# im.save(buffer, 'ppm')
# bytes_io = BytesIO()
# bytes_io.write(buffer.data())
# buffer.close()
# bytes_io.seek(0)
# return Image.open(bytes_io)
def align8to32(bytes, width, mode):
"""
converts each scanline of data from 8 bit to 32 bit aligned
"""
bits_per_pixel = {
'1': 1,
'L': 8,
'P': 8,
}[mode]
# calculate bytes per line and the extra padding if needed
bits_per_line = bits_per_pixel * width
full_bytes_per_line, remaining_bits_per_line = divmod(bits_per_line, 8)
bytes_per_line = full_bytes_per_line + (1 if remaining_bits_per_line else 0)
extra_padding = -bytes_per_line % 4
# already 32 bit aligned by luck
if not extra_padding:
return bytes
new_data = []
for i in range(len(bytes) // bytes_per_line):
new_data.append(bytes[i*bytes_per_line:(i+1)*bytes_per_line]
+ b'\x00' * extra_padding)
return b''.join(new_data)
def _toqclass_helper(im):
data = None
colortable = None
# handle filename, if given instead of image name
if hasattr(im, "toUtf8"):
# FIXME - is this really the best way to do this?
if py3:
im = str(im.toUtf8(), "utf-8")
else:
im = unicode(im.toUtf8(), "utf-8") # noqa: F821
if isPath(im):
im = Image.open(im)
if im.mode == "1":
format = QImage.Format_Mono
elif im.mode == "L":
format = QImage.Format_Indexed8
colortable = []
for i in range(256):
colortable.append(rgb(i, i, i))
elif im.mode == "P":
format = QImage.Format_Indexed8
colortable = []
palette = im.getpalette()
for i in range(0, len(palette), 3):
colortable.append(rgb(*palette[i:i+3]))
elif im.mode == "RGB":
data = im.tobytes("raw", "BGRX")
format = QImage.Format_RGB32
elif im.mode == "RGBA":
try:
data = im.tobytes("raw", "BGRA")
except SystemError:
# workaround for earlier versions
r, g, b, a = im.split()
im = Image.merge("RGBA", (b, g, r, a))
format = QImage.Format_ARGB32
else:
raise ValueError("unsupported image mode %r" % im.mode)
__data = data or align8to32(im.tobytes(), im.size[0], im.mode)
return {
'data': __data, 'im': im, 'format': format, 'colortable': colortable
}
if qt_is_installed:
class ImageQt(QImage):
def __init__(self, im):
"""
An PIL image wrapper for Qt. This is a subclass of PyQt's QImage
class.
:param im: A PIL Image object, or a file name (given either as
Python string or a PyQt string object).
"""
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,
self.__data, im_data['im'].size[0],
im_data['im'].size[1], im_data['format'])
if im_data['colortable']:
self.setColorTable(im_data['colortable'])
def toqimage(im):
return ImageQt(im)
def toqpixmap(im):
# # This doesn't work. For now using a dumb approach.
# im_data = _toqclass_helper(im)
# result = QPixmap(im_data['im'].size[0], im_data['im'].size[1])
# result.loadFromData(im_data['data'])
# Fix some strange bug that causes
if im.mode == 'RGB':
im = im.convert('RGBA')
qimage = toqimage(im)
return QPixmap.fromImage(qimage)

View File

@ -0,0 +1,56 @@
#
# The Python Imaging Library.
# $Id$
#
# sequence support classes
#
# history:
# 1997-02-20 fl Created
#
# Copyright (c) 1997 by Secret Labs AB.
# Copyright (c) 1997 by Fredrik Lundh.
#
# See the README file for information on usage and redistribution.
#
##
class Iterator(object):
"""
This class implements an iterator object that can be used to loop
over an image sequence.
You can use the ``[]`` operator to access elements by index. This operator
will raise an :py:exc:`IndexError` if you try to access a nonexistent
frame.
:param im: An image object.
"""
def __init__(self, im):
if not hasattr(im, "seek"):
raise AttributeError("im must have seek method")
self.im = im
self.position = 0
def __getitem__(self, ix):
try:
self.im.seek(ix)
return self.im
except EOFError:
raise IndexError # end of sequence
def __iter__(self):
return self
def __next__(self):
try:
self.im.seek(self.position)
self.position += 1
return self.im
except EOFError:
raise StopIteration
def next(self):
return self.__next__()

View File

@ -0,0 +1,223 @@
#
# The Python Imaging Library.
# $Id$
#
# im.show() drivers
#
# History:
# 2008-04-06 fl Created
#
# Copyright (c) Secret Labs AB 2008.
#
# See the README file for information on usage and redistribution.
#
from __future__ import print_function
from PIL import Image
import os
import sys
import subprocess
import tempfile
if sys.version_info.major >= 3:
from shlex import quote
else:
from pipes import quote
_viewers = []
def register(viewer, order=1):
try:
if issubclass(viewer, Viewer):
viewer = viewer()
except TypeError:
pass # raised if viewer wasn't a class
if order > 0:
_viewers.append(viewer)
elif order < 0:
_viewers.insert(0, viewer)
def show(image, title=None, **options):
r"""
Display a given image.
:param image: An image object.
:param title: Optional title. Not all viewers can display the title.
:param \**options: Additional viewer options.
:returns: True if a suitable viewer was found, false otherwise.
"""
for viewer in _viewers:
if viewer.show(image, title=title, **options):
return 1
return 0
class Viewer(object):
"""Base class for viewers."""
# main api
def show(self, image, **options):
# save temporary image to disk
if image.mode[:4] == "I;16":
# @PIL88 @PIL101
# "I;16" isn't an 'official' mode, but we still want to
# provide a simple way to show 16-bit images.
base = "L"
# FIXME: auto-contrast if max() > 255?
else:
base = Image.getmodebase(image.mode)
if base != image.mode and image.mode != "1" and image.mode != "RGBA":
image = image.convert(base)
return self.show_image(image, **options)
# hook methods
format = None
options = {}
def get_format(self, image):
"""Return format name, or None to save as PGM/PPM"""
return self.format
def get_command(self, file, **options):
raise NotImplementedError
def save_image(self, image):
"""Save to temporary file, and return filename"""
return image._dump(format=self.get_format(image), **self.options)
def show_image(self, image, **options):
"""Display given image"""
return self.show_file(self.save_image(image), **options)
def show_file(self, file, **options):
"""Display given file"""
os.system(self.get_command(file, **options))
return 1
# --------------------------------------------------------------------
if sys.platform == "win32":
class WindowsViewer(Viewer):
format = "BMP"
def get_command(self, file, **options):
return ('start "Pillow" /WAIT "%s" '
'&& ping -n 2 127.0.0.1 >NUL '
'&& del /f "%s"' % (file, file))
register(WindowsViewer)
elif sys.platform == "darwin":
class MacViewer(Viewer):
format = "PNG"
options = {'compress_level': 1}
def get_command(self, file, **options):
# on darwin open returns immediately resulting in the temp
# file removal while app is opening
command = "open -a /Applications/Preview.app"
command = "(%s %s; sleep 20; rm -f %s)&" % (command, quote(file),
quote(file))
return command
def show_file(self, file, **options):
"""Display given file"""
fd, path = tempfile.mkstemp()
with os.fdopen(fd, 'w') as f:
f.write(file)
with open(path, "r") as f:
subprocess.Popen([
'im=$(cat);'
'open -a /Applications/Preview.app $im;'
'sleep 20;'
'rm -f $im'
], shell=True, stdin=f)
os.remove(path)
return 1
register(MacViewer)
else:
# unixoids
def which(executable):
path = os.environ.get("PATH")
if not path:
return None
for dirname in path.split(os.pathsep):
filename = os.path.join(dirname, executable)
if os.path.isfile(filename) and os.access(filename, os.X_OK):
return filename
return None
class UnixViewer(Viewer):
format = "PNG"
options = {'compress_level': 1}
def get_command(self, file, **options):
command = self.get_command_ex(file, **options)[0]
return "(%s %s; rm -f %s)&" % (command, quote(file), quote(file))
def show_file(self, file, **options):
"""Display given file"""
fd, path = tempfile.mkstemp()
with os.fdopen(fd, 'w') as f:
f.write(file)
with open(path, "r") as f:
command = self.get_command_ex(file, **options)[0]
subprocess.Popen([
'im=$(cat);' +
command+' $im;'
'rm -f $im'
], shell=True, stdin=f)
os.remove(path)
return 1
# implementations
class DisplayViewer(UnixViewer):
def get_command_ex(self, file, **options):
command = executable = "display"
return command, executable
if which("display"):
register(DisplayViewer)
class EogViewer(UnixViewer):
def get_command_ex(self, file, **options):
command = executable = "eog"
return command, executable
if which("eog"):
register(EogViewer)
class XVViewer(UnixViewer):
def get_command_ex(self, file, title=None, **options):
# note: xv is pretty outdated. most modern systems have
# imagemagick's display command instead.
command = executable = "xv"
if title:
command += " -name %s" % quote(title)
return command, executable
if which("xv"):
register(XVViewer)
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Syntax: python ImageShow.py imagefile [title]")
sys.exit()
print(show(Image.open(sys.argv[1]), *sys.argv[2:]))

View File

@ -0,0 +1,148 @@
#
# The Python Imaging Library.
# $Id$
#
# global image statistics
#
# History:
# 1996-04-05 fl Created
# 1997-05-21 fl Added mask; added rms, var, stddev attributes
# 1997-08-05 fl Added median
# 1998-07-05 hk Fixed integer overflow error
#
# Notes:
# This class shows how to implement delayed evaluation of attributes.
# To get a certain value, simply access the corresponding attribute.
# The __getattr__ dispatcher takes care of the rest.
#
# Copyright (c) Secret Labs AB 1997.
# Copyright (c) Fredrik Lundh 1996-97.
#
# See the README file for information on usage and redistribution.
#
import math
import operator
import functools
class Stat(object):
def __init__(self, image_or_list, mask=None):
try:
if mask:
self.h = image_or_list.histogram(mask)
else:
self.h = image_or_list.histogram()
except AttributeError:
self.h = image_or_list # assume it to be a histogram list
if not isinstance(self.h, list):
raise TypeError("first argument must be image or list")
self.bands = list(range(len(self.h) // 256))
def __getattr__(self, id):
"Calculate missing attribute"
if id[:4] == "_get":
raise AttributeError(id)
# calculate missing attribute
v = getattr(self, "_get" + id)()
setattr(self, id, v)
return v
def _getextrema(self):
"Get min/max values for each band in the image"
def minmax(histogram):
n = 255
x = 0
for i in range(256):
if histogram[i]:
n = min(n, i)
x = max(x, i)
return n, x # returns (255, 0) if there's no data in the histogram
v = []
for i in range(0, len(self.h), 256):
v.append(minmax(self.h[i:]))
return v
def _getcount(self):
"Get total number of pixels in each layer"
v = []
for i in range(0, len(self.h), 256):
v.append(functools.reduce(operator.add, self.h[i:i+256]))
return v
def _getsum(self):
"Get sum of all pixels in each layer"
v = []
for i in range(0, len(self.h), 256):
layerSum = 0.0
for j in range(256):
layerSum += j * self.h[i + j]
v.append(layerSum)
return v
def _getsum2(self):
"Get squared sum of all pixels in each layer"
v = []
for i in range(0, len(self.h), 256):
sum2 = 0.0
for j in range(256):
sum2 += (j ** 2) * float(self.h[i + j])
v.append(sum2)
return v
def _getmean(self):
"Get average pixel level for each layer"
v = []
for i in self.bands:
v.append(self.sum[i] / self.count[i])
return v
def _getmedian(self):
"Get median pixel level for each layer"
v = []
for i in self.bands:
s = 0
half = self.count[i]//2
b = i * 256
for j in range(256):
s = s + self.h[b+j]
if s > half:
break
v.append(j)
return v
def _getrms(self):
"Get RMS for each layer"
v = []
for i in self.bands:
v.append(math.sqrt(self.sum2[i] / self.count[i]))
return v
def _getvar(self):
"Get variance for each layer"
v = []
for i in self.bands:
n = self.count[i]
v.append((self.sum2[i]-(self.sum[i]**2.0)/n)/n)
return v
def _getstddev(self):
"Get standard deviation for each layer"
v = []
for i in self.bands:
v.append(math.sqrt(self.var[i]))
return v
Global = Stat # compatibility

View File

@ -0,0 +1,301 @@
#
# The Python Imaging Library.
# $Id$
#
# a Tk display interface
#
# History:
# 96-04-08 fl Created
# 96-09-06 fl Added getimage method
# 96-11-01 fl Rewritten, removed image attribute and crop method
# 97-05-09 fl Use PyImagingPaste method instead of image type
# 97-05-12 fl Minor tweaks to match the IFUNC95 interface
# 97-05-17 fl Support the "pilbitmap" booster patch
# 97-06-05 fl Added file= and data= argument to image constructors
# 98-03-09 fl Added width and height methods to Image classes
# 98-07-02 fl Use default mode for "P" images without palette attribute
# 98-07-02 fl Explicitly destroy Tkinter image objects
# 99-07-24 fl Support multiple Tk interpreters (from Greg Couch)
# 99-07-26 fl Automatically hook into Tkinter (if possible)
# 99-08-15 fl Hook uses _imagingtk instead of _imaging
#
# Copyright (c) 1997-1999 by Secret Labs AB
# Copyright (c) 1996-1997 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
import sys
from io import BytesIO
from . import Image
if sys.version_info.major > 2:
import tkinter
else:
import Tkinter as tkinter
# --------------------------------------------------------------------
# Check for Tkinter interface hooks
_pilbitmap_ok = None
def _pilbitmap_check():
global _pilbitmap_ok
if _pilbitmap_ok is None:
try:
im = Image.new("1", (1, 1))
tkinter.BitmapImage(data="PIL:%d" % im.im.id)
_pilbitmap_ok = 1
except tkinter.TclError:
_pilbitmap_ok = 0
return _pilbitmap_ok
def _get_image_from_kw(kw):
source = None
if "file" in kw:
source = kw.pop("file")
elif "data" in kw:
source = BytesIO(kw.pop("data"))
if source:
return Image.open(source)
# --------------------------------------------------------------------
# PhotoImage
class PhotoImage(object):
"""
A Tkinter-compatible photo image. This can be used
everywhere Tkinter expects an image object. If the image is an RGBA
image, pixels having alpha 0 are treated as transparent.
The constructor takes either a PIL image, or a mode and a size.
Alternatively, you can use the **file** or **data** options to initialize
the photo image object.
:param image: Either a PIL image, or a mode string. If a mode string is
used, a size must also be given.
:param size: If the first argument is a mode string, this defines the size
of the image.
:keyword file: A filename to load the image from (using
``Image.open(file)``).
:keyword data: An 8-bit string containing image data (as loaded from an
image file).
"""
def __init__(self, image=None, size=None, **kw):
# Tk compatibility: file or data
if image is None:
image = _get_image_from_kw(kw)
if hasattr(image, "mode") and hasattr(image, "size"):
# got an image instead of a mode
mode = image.mode
if mode == "P":
# palette mapped data
image.load()
try:
mode = image.palette.mode
except AttributeError:
mode = "RGB" # default
size = image.size
kw["width"], kw["height"] = size
else:
mode = image
image = None
if mode not in ["1", "L", "RGB", "RGBA"]:
mode = Image.getmodebase(mode)
self.__mode = mode
self.__size = size
self.__photo = tkinter.PhotoImage(**kw)
self.tk = self.__photo.tk
if image:
self.paste(image)
def __del__(self):
name = self.__photo.name
self.__photo.name = None
try:
self.__photo.tk.call("image", "delete", name)
except Exception:
pass # ignore internal errors
def __str__(self):
"""
Get the Tkinter photo image identifier. This method is automatically
called by Tkinter whenever a PhotoImage object is passed to a Tkinter
method.
:return: A Tkinter photo image identifier (a string).
"""
return str(self.__photo)
def width(self):
"""
Get the width of the image.
:return: The width, in pixels.
"""
return self.__size[0]
def height(self):
"""
Get the height of the image.
:return: The height, in pixels.
"""
return self.__size[1]
def paste(self, im, box=None):
"""
Paste a PIL image into the photo image. Note that this can
be very slow if the photo image is displayed.
:param im: A PIL image. The size must match the target region. If the
mode does not match, the image is converted to the mode of
the bitmap image.
:param box: A 4-tuple defining the left, upper, right, and lower pixel
coordinate. See :ref:`coordinate-system`. If None is given
instead of a tuple, all of the image is assumed.
"""
# convert to blittable
im.load()
image = im.im
if image.isblock() and im.mode == self.__mode:
block = image
else:
block = image.new_block(self.__mode, im.size)
image.convert2(block, image) # convert directly between buffers
tk = self.__photo.tk
try:
tk.call("PyImagingPhoto", self.__photo, block.id)
except tkinter.TclError:
# activate Tkinter hook
try:
from . import _imagingtk
try:
if hasattr(tk, 'interp'):
# Required for PyPy, which always has CFFI installed
from cffi import FFI
ffi = FFI()
# PyPy is using an 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:
_imagingtk.tkinit(id(tk), 0)
tk.call("PyImagingPhoto", self.__photo, block.id)
except (ImportError, AttributeError, tkinter.TclError):
raise # configuration problem; cannot attach to Tkinter
# --------------------------------------------------------------------
# BitmapImage
class BitmapImage(object):
"""
A Tkinter-compatible bitmap image. This can be used everywhere Tkinter
expects an image object.
The given image must have mode "1". Pixels having value 0 are treated as
transparent. Options, if any, are passed on to Tkinter. The most commonly
used option is **foreground**, which is used to specify the color for the
non-transparent parts. See the Tkinter documentation for information on
how to specify colours.
:param image: A PIL image.
"""
def __init__(self, image=None, **kw):
# Tk compatibility: file or data
if image is None:
image = _get_image_from_kw(kw)
self.__mode = image.mode
self.__size = image.size
if _pilbitmap_check():
# fast way (requires the pilbitmap booster patch)
image.load()
kw["data"] = "PIL:%d" % image.im.id
self.__im = image # must keep a reference
else:
# slow but safe way
kw["data"] = image.tobitmap()
self.__photo = tkinter.BitmapImage(**kw)
def __del__(self):
name = self.__photo.name
self.__photo.name = None
try:
self.__photo.tk.call("image", "delete", name)
except Exception:
pass # ignore internal errors
def width(self):
"""
Get the width of the image.
:return: The width, in pixels.
"""
return self.__size[0]
def height(self):
"""
Get the height of the image.
:return: The height, in pixels.
"""
return self.__size[1]
def __str__(self):
"""
Get the Tkinter bitmap image identifier. This method is automatically
called by Tkinter whenever a BitmapImage object is passed to a Tkinter
method.
:return: A Tkinter bitmap image identifier (a string).
"""
return str(self.__photo)
def getimage(photo):
""" This function is unimplemented """
"""Copies the contents of a PhotoImage to a PIL image memory."""
photo.tk.call("PyImagingPhotoGet", photo)
def _show(image, title):
"""Helper for the Image.show method."""
class UI(tkinter.Label):
def __init__(self, master, im):
if im.mode == "1":
self.image = BitmapImage(im, foreground="white", master=master)
else:
self.image = PhotoImage(im, master=master)
tkinter.Label.__init__(self, master, image=self.image,
bg="black", bd=0)
if not tkinter._default_root:
raise IOError("tkinter not initialized")
top = tkinter.Toplevel()
if title:
top.title(title)
UI(top, image).pack()

View File

@ -0,0 +1,98 @@
#
# The Python Imaging Library.
# $Id$
#
# transform wrappers
#
# History:
# 2002-04-08 fl Created
#
# Copyright (c) 2002 by Secret Labs AB
# Copyright (c) 2002 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
from . import Image
class Transform(Image.ImageTransformHandler):
def __init__(self, data):
self.data = data
def getdata(self):
return self.method, self.data
def transform(self, size, image, **options):
# can be overridden
method, data = self.getdata()
return image.transform(size, method, data, **options)
class AffineTransform(Transform):
"""
Define an affine image transform.
This function takes a 6-tuple (a, b, c, d, e, f) which contain the first
two rows from an affine transform matrix. For each pixel (x, y) in the
output image, the new value is taken from a position (a x + b y + c,
d x + e y + f) in the input image, rounded to nearest pixel.
This function can be used to scale, translate, rotate, and shear the
original image.
See :py:meth:`~PIL.Image.Image.transform`
:param matrix: A 6-tuple (a, b, c, d, e, f) containing the first two rows
from an affine transform matrix.
"""
method = Image.AFFINE
class ExtentTransform(Transform):
"""
Define a transform to extract a subregion from an image.
Maps a rectangle (defined by two corners) from the image to a rectangle of
the given size. The resulting image will contain data sampled from between
the corners, such that (x0, y0) in the input image will end up at (0,0) in
the output image, and (x1, y1) at size.
This method can be used to crop, stretch, shrink, or mirror an arbitrary
rectangle in the current image. It is slightly slower than crop, but about
as fast as a corresponding resize operation.
See :py:meth:`~PIL.Image.Image.transform`
:param bbox: A 4-tuple (x0, y0, x1, y1) which specifies two points in the
input image's coordinate system. See :ref:`coordinate-system`.
"""
method = Image.EXTENT
class QuadTransform(Transform):
"""
Define a quad image transform.
Maps a quadrilateral (a region defined by four corners) from the image to a
rectangle of the given size.
See :py:meth:`~PIL.Image.Image.transform`
:param xy: An 8-tuple (x0, y0, x1, y1, x2, y2, x3, y3) which contain the
upper left, lower left, lower right, and upper right corner of the
source quadrilateral.
"""
method = Image.QUAD
class MeshTransform(Transform):
"""
Define a mesh image transform. A mesh transform consists of one or more
individual quad transforms.
See :py:meth:`~PIL.Image.Image.transform`
:param data: A list of (bbox, quad) tuples.
"""
method = Image.MESH

View File

@ -0,0 +1,228 @@
#
# The Python Imaging Library.
# $Id$
#
# a Windows DIB display interface
#
# History:
# 1996-05-20 fl Created
# 1996-09-20 fl Fixed subregion exposure
# 1997-09-21 fl Added draw primitive (for tzPrint)
# 2003-05-21 fl Added experimental Window/ImageWindow classes
# 2003-09-05 fl Added fromstring/tostring methods
#
# Copyright (c) Secret Labs AB 1997-2003.
# Copyright (c) Fredrik Lundh 1996-2003.
#
# See the README file for information on usage and redistribution.
#
from . import Image
class HDC(object):
"""
Wraps an HDC integer. The resulting object can be passed to the
:py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose`
methods.
"""
def __init__(self, dc):
self.dc = dc
def __int__(self):
return self.dc
class HWND(object):
"""
Wraps an HWND integer. The resulting object can be passed to the
:py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose`
methods, instead of a DC.
"""
def __init__(self, wnd):
self.wnd = wnd
def __int__(self):
return self.wnd
class Dib(object):
"""
A Windows bitmap with the given mode and size. The mode can be one of "1",
"L", "P", or "RGB".
If the display requires a palette, this constructor creates a suitable
palette and associates it with the image. For an "L" image, 128 greylevels
are allocated. For an "RGB" image, a 6x6x6 colour cube is used, together
with 20 greylevels.
To make sure that palettes work properly under Windows, you must call the
**palette** method upon certain events from Windows.
:param image: Either a PIL image, or a mode string. If a mode string is
used, a size must also be given. The mode can be one of "1",
"L", "P", or "RGB".
:param size: If the first argument is a mode string, this
defines the size of the image.
"""
def __init__(self, image, size=None):
if hasattr(image, "mode") and hasattr(image, "size"):
mode = image.mode
size = image.size
else:
mode = image
image = None
if mode not in ["1", "L", "P", "RGB"]:
mode = Image.getmodebase(mode)
self.image = Image.core.display(mode, size)
self.mode = mode
self.size = size
if image:
self.paste(image)
def expose(self, handle):
"""
Copy the bitmap contents to a device context.
:param handle: Device context (HDC), cast to a Python integer, or an
HDC or HWND instance. In PythonWin, you can use the
:py:meth:`CDC.GetHandleAttrib` to get a suitable handle.
"""
if isinstance(handle, HWND):
dc = self.image.getdc(handle)
try:
result = self.image.expose(dc)
finally:
self.image.releasedc(handle, dc)
else:
result = self.image.expose(handle)
return result
def draw(self, handle, dst, src=None):
"""
Same as expose, but allows you to specify where to draw the image, and
what part of it to draw.
The destination and source areas are given as 4-tuple rectangles. If
the source is omitted, the entire image is copied. If the source and
the destination have different sizes, the image is resized as
necessary.
"""
if not src:
src = (0, 0) + self.size
if isinstance(handle, HWND):
dc = self.image.getdc(handle)
try:
result = self.image.draw(dc, dst, src)
finally:
self.image.releasedc(handle, dc)
else:
result = self.image.draw(handle, dst, src)
return result
def query_palette(self, handle):
"""
Installs the palette associated with the image in the given device
context.
This method should be called upon **QUERYNEWPALETTE** and
**PALETTECHANGED** events from Windows. If this method returns a
non-zero value, one or more display palette entries were changed, and
the image should be redrawn.
:param handle: Device context (HDC), cast to a Python integer, or an
HDC or HWND instance.
:return: A true value if one or more entries were changed (this
indicates that the image should be redrawn).
"""
if isinstance(handle, HWND):
handle = self.image.getdc(handle)
try:
result = self.image.query_palette(handle)
finally:
self.image.releasedc(handle, handle)
else:
result = self.image.query_palette(handle)
return result
def paste(self, im, box=None):
"""
Paste a PIL image into the bitmap image.
:param im: A PIL image. The size must match the target region.
If the mode does not match, the image is converted to the
mode of the bitmap image.
:param box: A 4-tuple defining the left, upper, right, and
lower pixel coordinate. See :ref:`coordinate-system`. If
None is given instead of a tuple, all of the image is
assumed.
"""
im.load()
if self.mode != im.mode:
im = im.convert(self.mode)
if box:
self.image.paste(im.im, box)
else:
self.image.paste(im.im)
def frombytes(self, buffer):
"""
Load display memory contents from byte data.
:param buffer: A buffer containing display data (usually
data returned from <b>tobytes</b>)
"""
return self.image.frombytes(buffer)
def tobytes(self):
"""
Copy display memory contents to bytes object.
:return: A bytes object containing display data.
"""
return self.image.tobytes()
class Window(object):
"""Create a Window with the given title size."""
def __init__(self, title="PIL", width=None, height=None):
self.hwnd = Image.core.createwindow(
title, self.__dispatcher, width or 0, height or 0
)
def __dispatcher(self, action, *args):
return getattr(self, "ui_handle_" + action)(*args)
def ui_handle_clear(self, dc, x0, y0, x1, y1):
pass
def ui_handle_damage(self, x0, y0, x1, y1):
pass
def ui_handle_destroy(self):
pass
def ui_handle_repair(self, dc, x0, y0, x1, y1):
pass
def ui_handle_resize(self, width, height):
pass
def mainloop(self):
Image.core.eventloop()
class ImageWindow(Window):
"""Create an image window which displays the given image."""
def __init__(self, image, title="PIL"):
if not isinstance(image, Dib):
image = Dib(image)
self.image = image
width, height = image.size
Window.__init__(self, title, width=width, height=height)
def ui_handle_repair(self, dc, x0, y0, x1, y1):
self.image.draw(dc, (x0, y0, x1, y1))

Some files were not shown because too many files have changed in this diff Show More