Author: georg.brandl
Date: Sat Jun 30 21:07:03 2007
New Revision: 56133
Added:
peps/trunk/pep-0368.txt (contents, props changed)
Modified:
peps/trunk/pep-0000.txt
Log:
Add PEP 368, ``Standard image protocol and class'', by L. Mastrodomenico.
Modified: peps/trunk/pep-0000.txt
==============================================================================
--- peps/trunk/pep-0000.txt (original)
+++ peps/trunk/pep-0000.txt Sat Jun 30 21:07:03 2007
@@ -96,6 +96,7 @@
S 364 Transitioning to the Py3K Standard Library Warsaw
S 365 Adding the pkg_resources module Eby
S 366 Main module explicit relative imports Coghlan
+ S 368 Standard image protocol and class Mastrodomenico
S 3101 Advanced String Formatting Talin
S 3108 Standard Library Reorganization Cannon
S 3116 New I/O Stutzbach, Verdone, GvR
@@ -464,6 +465,7 @@
S 365 Adding the pkg_resources module Eby
S 366 Main module explicit relative imports Coghlan
SR 367 New Super Spealman, Delaney
+ S 368 Standard image protocol and class Mastrodomenico
SR 666 Reject Foolish Indentation Creighton
SR 754 IEEE 754 Floating Point Special Values Warnes
P 3000 Python 3000 GvR
@@ -584,6 +586,7 @@
von Löwis, Martin martin(a)v.loewis.de
Lownds, Tony tony(a)pagedna.com
Martelli, Alex aleaxit(a)gmail.com
+ Mastrodomenico, Lino l.mastrodomenico(a)gmail.com
Maupin, Patrick pmaupin(a)gmail.com
McClelland, Andrew eternalsquire(a)comcast.net
McMillan, Gordon gmcm(a)hypernet.com
Added: peps/trunk/pep-0368.txt
==============================================================================
--- (empty file)
+++ peps/trunk/pep-0368.txt Sat Jun 30 21:07:03 2007
@@ -0,0 +1,844 @@
+PEP: 368
+Title: Standard image protocol and class
+Version: $Revision$
+Last-Modified: $Date$
+Author: Lino Mastrodomenico <l.mastrodomenico(a)gmail.com>
+Status: Draft
+Type: Standards Track
+Content-Type: text/x-rst
+Created: 28-Jun-2007
+Python-Version: 2.6, 3.0
+Post-History:
+
+
+Abstract
+========
+
+The current situation of image storage and manipulation in the Python
+world is extremely fragmented: almost every library that uses image
+objects has implemented its own image class, incompatible with
+everyone else's and often not very pythonic. A basic RGB image class
+exists in the standard library (``Tkinter.PhotoImage``), but is pretty
+much unusable, and unused, for anything except Tkinter programming.
+
+This fragmentation not only takes up valuable space in the developers
+minds, but also makes the exchange of images between different
+libraries (needed in relatively common use cases) slower and more
+complex than it needs to be.
+
+This PEP proposes to improve the situation by defining a simple and
+pythonic image protocol/interface that can be hopefully accepted and
+implemented by existing image classes inside and outside the standard
+library *without breaking backward compatibility* with their existing
+user bases. In practice this is a definition of how a minimal
+*image-like* object should look and act (in a similar way to the
+``read()`` and ``write()`` methods in *file-like* objects).
+
+The inclusion in the standard library of a class that provides basic
+image manipulation functionality and implements the new protocol is
+also proposed, together with a mixin class that helps adding support
+for the protocol to existing image classes.
+
+
+Rationale
+=========
+
+A good way to have high quality modules ready for inclusion in the
+Python standard library is to simply wait for natural selection among
+competing external libraries to provide a clear winner with useful
+functionality and a big user base. Then the de-facto standard can be
+officially sanctioned by including it in the standard library.
+
+Unfortunately this approach hasn't worked well for the creation of a
+dominant image class in the Python world: almost every third-party
+library that requires an image object creates its own class
+incompatible with the ones from other libraries. This is a real
+problem because it's entirely reasonable for a program to create and
+manipulate an image using, e.g., PIL (the Python Imaging Library) and
+then display it using wxPython or pygame. But these libraries have
+different and incompatible image classes, and the usual solution is to
+manually "export" an image from the source to a (width, height,
+bytes_string) tuple and "import" it creating a new instance in the
+target format. This approach *works*, but is both uglier and slower
+than it needs to be.
+
+Another "solution" that has been sometimes used is the creation of
+specific adapters and/or converters from a class to another (e.g. PIL
+offers the ``ImageTk`` module for converting PIL images to a class
+compatible with the Tkinter one). But this approach doesn't scale
+well with the number of libraries involved and it's still annoying for
+the user: if I have a perfectly good image object why should I convert
+before passing it to the next method, why can't it simply accept my
+image as-is?
+
+The problem isn't by any stretch limited to the three mentioned
+libraries and has probably multiple causes, including two that IMO are
+very important to understand before solving it:
+
+* in today's computing world an image is a basic type not strictly
+ tied to a specific domain. This is why there will never be a clear
+ winner between the image classes from the three libraries mentioned
+ above (PIL, wxPython and pygame): they cover different domains and
+ don't really compete with each other;
+
+* the Python standard library has never provided a good image class
+ that can be adopted or imitated by third part modules.
+ ``Tkinter.PhotoImage`` provides basic RGB functionality, but it's by
+ far the slowest and ugliest of the bunch and it can be instantiated
+ only after the Tkinter root window has been created.
+
+This PEP tries to improve this situation in four ways:
+
+1. It defines a simple and pythonic image protocol/interface (both on
+ the Python and the C side) that can be hopefully accepted and
+ implemented by existing image classes inside and outside the
+ standard library *without breaking backward compatibility* with
+ their existing user bases.
+
+2. It proposes the inclusion in the standard library of three new
+ classes:
+
+ * ``ImageMixin`` provides almost everything necessary to implement
+ the new protocol; its main purpose is to make as simple as
+ possible to support this interface for existing libraries, in
+ some cases as simple as adding it to the list of base classes and
+ doing minor additions to the constructor.
+
+ * ``Image`` is a subclass of ``ImageMixin`` and will add a
+ constructor that can resize and/or convert an image between
+ different pixel formats. This is intended to provide a fast and
+ efficient default implementation of the new protocol.
+
+ * ``ImageSize`` is a minor helper class. See below for details.
+
+3. ``Tkinter.PhotoImage`` will implement the new protocol (mostly
+ through the ``ImageMixin`` class) and all the Tkinter methods that
+ can receive an image will be modified the accept any object that
+ implements the interface. As an aside the author of this PEP will
+ collaborate with the developers of the most common external
+ libraries to achieve the same goal (supporting the protocol in
+ their classes and accepting any class that implements it).
+
+4. New ``PyImage_*`` functions will be added to the CPython C API:
+ they implement the C side of the protocol and accept as first
+ parameter **any** object that supports it, even if it isn't an
+ instance of the ``Image``/``ImageMixin`` classes.
+
+The main effects for the end user will be a simplification of the
+interchange of images between different libraries (if everything goes
+well, any Python library will accept images from any other library)
+and the out-of-the-box availability of the new ``Image`` class. The
+new class is intended to cover simple but common use cases like
+cropping and/or resizing a photograph to the desired size and passing
+it an appropriate widget for displaying it on a window, or darkening a
+texture and passing it to a 3D library.
+
+The ``Image`` class is not intended to replace or compete with PIL,
+Pythonmagick or NumPy, even if it provides a (very small) subset of
+the functionality of these three libraries. In particular PIL offers
+very rich image manipulation features with *dozens* of classes,
+filters, transformations and file formats. The inclusion of PIL (or
+something similar) in the standard library may, or may not, be a
+worthy goal but it's completely outside the scope of this PEP.
+
+
+Specification
+=============
+
+The ``imageop`` module is used as the *default* location for the new
+classes and objects because it has for a long time hosted functions
+that provided a somewhat similar functionality, but a new module may
+be created if preferred (e.g. a new "``image``" or "``media``" module;
+the latter may eventually include other multimedia classes).
+
+``MODES`` is a new module level constant: it is a set of the pixel
+formats supported by the ``Image`` class. Any image object that
+implements the new protocol is guaranteed to be formatted in one of
+these modes, but libraries that accept images are allowed to support
+only a subset of them.
+
+These modes are in turn also available as module level constants (e.g.
+``imageop.RGB``).
+
+The following table is a summary of the modes currently supported and
+their properties:
+
+========= =============== ========= =========== ======================
+ Name Component Bits per Subsampling Valid
+ names component intervals
+========= =============== ========= =========== ======================
+L l (lowercase L) 8 no full range
+L16 l 16 no full range
+L32 l 32 no full range
+LA l, a 8 no full range
+LA32 l, a 16 no full range
+RGB r, g, b 8 no full range
+RGB48 r, g, b 16 no full range
+RGBA r, g, b, a 8 no full range
+RGBA64 r, g, b, a 16 no full range
+YV12 y, cr, cb 8 1, 2, 2 16-235, 16-240, 16-240
+JPEG_YV12 y, cr, cb 8 1, 2, 2 full range
+CMYK c, m, y, k 8 no full range
+CMYK64 c, m, y, k 16 no full range
+========= =============== ========= =========== ======================
+
+When the name of a mode ends with a number, it represents the average
+number of bits per pixel. All the other modes simply use a byte per
+component per pixel.
+
+No palette modes or modes with less than 8 bits per component are
+supported. Welcome to the 21st century.
+
+Here's a quick description of the modes and the rationale for their
+inclusion; there are four groups of modes:
+
+1. **grayscale** (``L*`` modes): they are heavily used in scientific
+ computing (those people may also need a very high dynamic range and
+ precision, hence ``L32``, the only mode with 32 bits per component)
+ and sometimes it can be useful to consider a single component of a
+ color image as a grayscale image (this is used by the individual
+ planes of the planar images, see ``YV12`` below); the name of the
+ component (``'l'``, lowercase letter L) stands for luminance, the
+ second optional component (``'a'``) is the alpha value and
+ represents the opacity of the pixels: alpha = 0 means full
+ transparency, alpha = 255/65535 represents a fully opaque pixel;
+
+2. **RGB\* modes**: the garden variety color images. The optional
+ alpha component has the same meaning as in grayscale modes;
+
+3. **YCbCr**, a.k.a. YUV (``*YV12`` modes). These modes are planar
+ (i.e. the values of all the pixel for each component are stored in
+ a consecutive memory area, instead of the usual arrangement where
+ all the components of a pixel reside in consecutive bytes) and use
+ a 1, 2, 2 (a.k.a. 4:2:0) subsampling (i.e. each pixel has its own Y
+ value, but the Cb and Cr components are shared between groups of
+ 2x2 adjacent pixels) because this is the format that's by far the
+ most common for YCbCr images. Please note that the V (Cr) plane is
+ stored before the U (Cb) plane.
+
+ ``YV12`` is commonly used for MPEG2 (including DVDs), MPEG4 (both
+ ASP/DivX and AVC/H.264) and Theora video frames. Valid values for
+ Y are in range(16, 236) (excluding 236), and valid values for Cb
+ and Cr are in range(16, 241). ``JPEG_YV12`` is similar to
+ ``YV12``, but the three components can have the full range of 256
+ values. It's the native format used by almost all JPEG/JFIF files
+ and by MJPEG video frames. The "strangeness" of these two wrt all
+ the other supported modes derives from the fact that they are
+ widely used that way by a lot of existing libraries and
+ applications; this is also the reason why they are included (and
+ the fact that they can't losslessly converted to RGB because YCbCr
+ is a bigger color space); the funny 4:2:0 planar arrangement of the
+ pixel values is relatively easy to support because in most cases
+ the three planes can be considered three separate grayscale images;
+
+4. **CMYK\* modes** (cyan, magenta, yellow and black) are subtractive
+ color modes, used for printing color images on dead trees.
+ Professional designers love to pretend that they can't live without
+ them, so here they are.
+
+
+Python API
+----------
+
+See the examples_ below.
+
+In Python 2.x, all the new classes defined here are new-style classes.
+
+
+Mode Objects
+''''''''''''
+
+The mode objects offer a number of attributes and methods that can be
+used for implementing generic algorithms that work on different types
+of images:
+
+``components``
+
+ The number of components per pixel (e.g. 4 for an RGBA image).
+
+``component_names``
+
+ A tuple of strings; see the column "Component names" in the above
+ table.
+
+``bits_per_component``
+
+ 8, 16 or 32; see "Bits per component" in the above table.
+
+``bytes_per_pixel``
+
+ ``components * bits_per_component // 8``, only available for non
+ planar modes (see below).
+
+``planar``
+
+ Boolean; ``True`` if the image components reside each in a
+ separate plane. Currently this happens if and only if the mode
+ uses subsampling.
+
+``subsampling``
+
+ A tuple that for each component in the mode contains a tuple of
+ two integers that represent the amount of downsampling in the
+ horizontal and vertical direction, respectively. In practice it's
+ ``((1, 1), (2, 2), (2, 2))`` for ``YV12`` and ``JPEG_YV12`` and
+ ``((1, 1),) * components`` for everything else.
+
+``x_divisor``
+
+ ``max(x for x, y in subsampling)``; the width of an image that
+ uses this mode must be divisible for this value.
+
+``y_divisor``
+
+ ``max(y for x, y in subsampling)``; the height of an image that
+ uses this mode must be divisible for this value.
+
+``intervals``
+
+ A tuple that for each component in the mode contains a tuple of
+ two integers: the minimum and maximum valid value for the
+ component. Its value is ``((16, 235), (16, 240), (16, 240))`` for
+ ``YV12`` and ``((0, 2 ** bits_per_component - 1),) * components``
+ for everything else.
+
+``get_length(iterable[integer]) -> int``
+
+ The parameter must be an iterable that contains two integers: the
+ width and height of an image; it returns the number of bytes
+ needed to store an image of these dimensions with this mode.
+
+Implementation detail: the modes are instances of a subclass of
+``str`` and have a value equal to their name (e.g. ``imageop.RGB ==
+'RGB'``) except for ``L32`` that has value ``'I'``. This is only
+intended for backward compatibility with existing PIL users; new code
+that uses the image protocol proposed here should not rely on this
+detail.
+
+
+Image Protocol
+''''''''''''''
+
+Any object that supports the image protocol must provide the following
+methods and attributes:
+
+``mode``
+
+ The format and the arrangement of the pixels in this image; it's
+ one of the constants in the ``MODES`` set.
+
+``size``
+
+ An instance of the `ImageSize class`_; it's a named tuple of two
+ integers: the width and the height of the image in pixels; both of
+ them must be >= 1 and can also be accessed as the ``width`` and
+ ``height`` attributes of ``size``.
+
+``buffer``
+
+ A sequence of integers between 0 and 255; they are the actual
+ bytes used for storing the image data (i.e. modifying their values
+ affects the image pixels and vice versa); the data has a
+ row-major/C-contiguous order without padding and without any
+ special memory alignment, even when there are more than 8 bits per
+ component. The only supported methods are ``__len__``,
+ ``__getitem__``/``__setitem__`` (with both integers and slice
+ indexes) and ``__iter__``; on the C side it implements the buffer
+ protocol.
+
+ This is a pretty low level interface to the image and the user is
+ responsible for using the correct (native) byte order for modes
+ with more than 8 bit per component and the correct value ranges
+ for ``YV12`` images. A buffer may or may not keep a reference to
+ its image, but it's still safe (if useless) to use the buffer even
+ after the corresponding image has been destroyed by the garbage
+ collector (this will require changes to the image class of
+ wxPython and possibly other libraries). Implementation detail:
+ this can be an ``array('B')``, a ``bytes()`` object or a
+ specialized fixed-length type.
+
+``info``
+
+ A ``dict`` object that can contain arbitrary metadata associated
+ with the image (e.g. DPI, gamma, ICC profile, exposure time...);
+ the interpretation of this data is beyond the scope of this PEP
+ and probably depends on the library used to create and/or to save
+ the image; if a method of the image returns a new image, it can
+ copy or adapt metadata from its own ``info`` attribute (the
+ ``ImageMixin`` implementation always creates a new image with an
+ empty ``info`` dictionary).
+
+| ``bits_per_component``
+| ``bytes_per_pixel``
+| ``component_names``
+| ``components``
+| ``intervals``
+| ``planar``
+| ``subsampling``
+
+ Shortcuts for the corresponding ``mode.*`` attributes.
+
+``map(function[, function...]) -> None``
+
+ For every pixel in the image, maps each component through the
+ corresponding function. If only one function is passed, it is
+ used repeatedly for each component. This method modifies the
+ image **in place** and is usually very fast (most of the time the
+ functions are called only a small number of times, possibly only
+ once for simple functions without branches), but it imposes a
+ number of restrictions on the function(s) passed:
+
+ * it must accept a single integer argument and return a number
+ (``map`` will round the result to the nearest integer and clip
+ it to ``range(0, 2 ** bits_per_component)``, if necessary);
+
+ * it must *not* try to intercept any ``BaseException``,
+ ``Exception`` or any unknown subclass of ``Exception`` raised by
+ any operation on the argument (implementations may try to
+ optimize the speed by passing funny objects, so even a simple
+ ``"if n == 10:"`` may raise an exception: simply ignore it,
+ ``map`` will take care of it); catching any other exception is
+ fine;
+
+ * it should be side-effect free and its result should not depend
+ on values (other than the argument) that may change during a
+ single invocation of ``map``.
+
+| ``rotate90() -> image``
+| ``rotate180() -> image``
+| ``rotate270() -> image``
+
+ Return a copy of the image rotated 90, 180 or 270 degrees
+ counterclockwise around its center.
+
+``clip() -> None``
+
+ Saturates invalid component values in ``YV12`` images to the
+ minimum or the maximum allowed (see ``mode.intervals``), for other
+ image modes this method does nothing, very fast; libraries that
+ save/export ``YV12`` images are encouraged to always call this
+ method, since intermediate operations (e.g. the ``map`` method)
+ may assign to pixels values outside the valid intervals.
+
+``split() -> tuple[image]``
+
+ Returns a tuple of ``L``, ``L16`` or ``L32`` images corresponding
+ to the individual components in the image.
+
+Planar images also supports attributes with the same names defined in
+``component_names``: they contain grayscale (mode ``L``) images that
+offer a view on the pixel values for the corresponding component; any
+change to the subimages is immediately reflected on the parent image
+and vice versa (their buffers refer to the same memory location).
+
+Non-planar images offer the following additional methods:
+
+``pixels() -> iterator[pixel]``
+
+ Returns an iterator that iterates over all the pixels in the
+ image, starting from the top line and scanning each line from left
+ to right. See below for a description of the `pixel objects`_.
+
+``__iter__() -> iterator[line]``
+
+ Returns an iterator that iterates over all the lines in the image,
+ from top to bottom. See below for a description of the `line
+ objects`_.
+
+``__len__() -> int``
+
+ Returns the number of lines in the image (``size.height``).
+
+``__getitem__(integer) -> line``
+
+ Returns the line at the specified (y) position.
+
+``__getitem__(tuple[integer]) -> pixel``
+
+ The parameter must be a tuple of two integers; they are
+ interpreted respectively as x and y coordinates in the image (0, 0
+ is the top left corner) and a pixel object is returned.
+
+``__getitem__(slice | tuple[integer | slice]) -> image``
+
+ The parameter must be a slice or a tuple that contains two slices
+ or an integer and a slice; the selected area of the image is
+ copied and a new image is returned; ``image[x:y:z]`` is equivalent
+ to ``image[:, x:y:z]``.
+
+``__setitem__(tuple[integer], integer | iterable[integer]) -> None``
+
+ Modifies the pixel at specified position; ``image[x, y] =
+ integer`` is a shortcut for ``image[x, y] = (integer,)`` for
+ images with a single component.
+
+``__setitem__(slice | tuple[integer | slice], image) -> None``
+
+ Selects an area in the same way as the corresponding form of the
+ ``__getitem__`` method and assigns to it a copy of the pixels from
+ the image in the second argument, that must have exactly the same
+ mode as this image and the same size as the specified area; the
+ alpha component, if present, is simply copied and doesn't affect
+ the other components of the image (i.e. no alpha compositing is
+ performed).
+
+The ``mode``, ``size`` and ``buffer`` (including the address in memory
+of the ``buffer``) never change after an image is created.
+
+It is expected that, if PEP 3118 is accepted, all the image objects
+will support the new buffer protocol, however this is beyond the scope
+of this PEP.
+
+
+``Image`` and ``ImageMixin`` Classes
+''''''''''''''''''''''''''''''''''''
+
+The ``ImageMixin`` class implements all the methods and attributes
+described above except ``mode``, ``size``, ``buffer`` and ``info``.
+``Image`` is a subclass of ``ImageMixin`` that adds support for these
+four attributes and offers the following constructor (please note that
+the constructor is not part of the image protocol):
+
+``__init__(mode, size, color, source)``
+
+ ``mode`` must be one of the constants in the ``MODES`` set,
+ ``size`` is a sequence of two integers (width and height of the
+ new image); ``color`` is a sequence of integers, one for each
+ component of the image, used to initialize all the pixels to the
+ same value; ``source`` can be a sequence of integers of the
+ appropriate size and format that is copied as-is in the buffer of
+ the new image or an existing image; in Python 2.x ``source`` can
+ also be an instance of ``str`` and is interpreted as a sequence of
+ bytes. ``color`` and ``source`` are mutually exclusive and if
+ they are both omitted the image is initialized to transparent
+ black (all the bytes in the buffer have value 16 in the ``YV12``
+ mode, 255 in the ``CMYK*`` modes and 0 for everything else). If
+ ``source`` is present and is an image, ``mode`` and/or ``size``
+ can be omitted; if they are specified and are different from the
+ source mode and/or size, the source image is converted.
+
+ The exact algorithms used for resizing and doing color space
+ conversions may differ between Python versions and
+ implementations, but they always give high quality results (e.g.:
+ a cubic spline interpolation can be used for upsampling and an
+ antialias filter can be used for downsampling images); any
+ combination of mode conversion is supported, but the algorithm
+ used for conversions to and from the ``CMYK*`` modes is pretty
+ naïve: if you have the exact color profiles of your devices you
+ may want to use a good color management tool such as LittleCMS.
+ The new image has an empty ``info`` ``dict``.
+
+
+Line Objects
+''''''''''''
+
+The line objects (returned, e.g., when iterating over an image)
+support the following attributes and methods:
+
+``mode``
+
+ The mode of the image from where this line comes.
+
+``__iter__() -> iterator[pixel]``
+
+ Returns an iterator that iterates over all the pixels in the line,
+ from left to right. See below for a description of the `pixel
+ objects`_.
+
+``__len__() -> int``
+
+ Returns the number of pixels in the line (the image width).
+
+``__getitem__(integer) -> pixel``
+
+ Returns the pixel at the specified (x) position.
+
+``__getitem__(slice) -> image``
+
+ The selected part of the line is copied and a new image is
+ returned; the new image will always have height 1.
+
+``__setitem__(integer, integer | iterable[integer]) -> None``
+
+ Modifies the pixel at the specified position; ``line[x] =
+ integer`` is a shortcut for ``line[x] = (integer,)`` for images
+ with a single component.
+
+``__setitem__(slice, image) -> None``
+
+ Selects a part of the line and assigns to it a copy of the pixels
+ from the image in the second argument, that must have height 1, a
+ width equal to the specified slice and the same mode as this line;
+ the alpha component, if present, is simply copied and doesn't
+ affect the other components of the image (i.e. no alpha
+ compositing is performed).
+
+
+Pixel Objects
+'''''''''''''
+
+The pixel objects (returned, e.g., when iterating over a line) support
+the following attributes and methods:
+
+``mode``
+
+ The mode of the image from where this pixel comes.
+
+``value``
+
+ A tuple of integers, one for each component. Any iterable of the
+ correct length can be assigned to ``value`` (it will be
+ automagically converted to a tuple), but you can't assign to it an
+ integer, even if the mode has only a single component: use, e.g.,
+ ``pixel.l = 123`` instead.
+
+``r, g, b, a, l, c, m, y, k``
+
+ The integer values of each component; only those applicable for
+ the current mode (in ``mode.component_names``) will be available.
+
+| ``__iter__() -> iterator[int]``
+| ``__len__() -> int``
+| ``__getitem__(integer | slice) -> int | tuple[int]``
+| ``__setitem__(integer | slice, integer | iterable[integer]) ->
+ None``
+
+ These four methods emulate a fixed length list of integers, one
+ for each pixel component.
+
+
+``ImageSize`` Class
+'''''''''''''''''''
+
+``ImageSize`` is a named tuple, a class identical to ``tuple`` except
+that:
+
+* its constructor only accepts two integers, width and height; they
+ are converted in the constructor using their ``__index__()``
+ methods, so all the ``ImageSize`` objects are guaranteed to contain
+ only ``int`` (or possibly ``long``, in Python 2.x) instances;
+
+* it has a ``width`` and a ``height`` property that are equivalent to
+ the first and the second number in the tuple, respectively;
+
+* the string returned by its ``__repr__`` method is
+ ``'imageop.ImageSize(width=%d, height=%d)' % (width, height)``.
+
+``ImageSize`` is not usually instantiated by end-users, but can be
+used when creating a new class that implements the image protocol,
+since the ``size`` attribute must be an ``ImageSize`` instance.
+
+
+C API
+-----
+
+The available image modes are visible at the C level as ``PyImage_*``
+constants of type ``PyObject *`` (e.g.: ``PyImage_RGB`` is
+``imageop.RGB``).
+
+The following functions offer a C-friendly interface to mode and image
+objects (all the functions return ``NULL`` or -1 on failure):
+
+``int PyImageMode_Check(PyObject *obj)``
+
+ Returns true if the object ``obj`` is a valid image mode.
+
+| ``int PyImageMode_GetComponents(PyObject *mode)``
+| ``PyObject* PyImageMode_GetComponentNames(PyObject *mode)``
+| ``int PyImageMode_GetBitsPerComponent(PyObject *mode)``
+| ``int PyImageMode_GetBytesPerPixel(PyObject *mode)``
+| ``int PyImageMode_GetPlanar(PyObject *mode)``
+| ``PyObject* PyImageMode_GetSubsampling(PyObject *mode)``
+| ``int PyImageMode_GetXDivisor(PyObject *mode)``
+| ``int PyImageMode_GetYDivisor(PyObject *mode)``
+| ``Py_ssize_t PyImageMode_GetLength(PyObject *mode, Py_ssize_t width,
+ Py_ssize_t height)``
+
+ These functions are equivalent to their corresponding Python
+ attributes or methods.
+
+``int PyImage_Check(PyObject *obj)``
+
+ Returns true if the object ``obj`` is an ``Image`` object or an
+ instance of a subtype of the ``Image`` type; see also
+ ``PyObject_CheckImage`` below.
+
+``int PyImage_CheckExact(PyObject *obj)``
+
+ Returns true if the object ``obj`` is an ``Image`` object, but not
+ an instance of a subtype of the ``Image`` type.
+
+| ``PyObject* PyImage_New(PyObject *mode, Py_ssize_t width,
+ Py_ssize_t height)``
+
+ Returns a new ``Image`` instance, initialized to transparent black
+ (see ``Image.__init__`` above for the details).
+
+| ``PyObject* PyImage_FromImage(PyObject *image, PyObject *mode,
+ Py_ssize_t width, Py_ssize_t height)``
+
+ Returns a new ``Image`` instance, initialized with the contents of
+ the ``image`` object rescaled and converted to the specified
+ ``mode``, if necessary.
+
+| ``PyObject* PyImage_FromBuffer(PyObject *buffer, PyObject *mode,
+ Py_ssize_t width,
+ Py_ssize_t height)``
+
+ Returns a new ``Image`` instance, initialized with the contents of
+ the ``buffer`` object.
+
+``int PyObject_CheckImage(PyObject *obj)``
+
+ Returns true if the object ``obj`` implements a sufficient subset
+ of the image protocol to be accepted by the functions defined
+ below, even if its class is not a subclass of ``ImageMixin``
+ and/or ``Image``. Currently it simply checks for the existence
+ and correctness of the attributes ``mode``, ``size`` and
+ ``buffer``.
+
+| ``PyObject* PyImage_GetMode(PyObject *image)``
+| ``Py_ssize_t PyImage_GetWidth(PyObject *image)``
+| ``Py_ssize_t PyImage_GetHeight(PyObject *image)``
+| ``int PyImage_Clip(PyObject *image)``
+| ``PyObject* PyImage_Split(PyObject *image)``
+| ``PyObject* PyImage_GetBuffer(PyObject *image)``
+| ``int PyImage_AsBuffer(PyObject *image, const void **buffer,
+ Py_ssize_t *buffer_len)``
+
+ These functions are equivalent to their corresponding Python
+ attributes or methods; the image memory can be accessed only with
+ the GIL and a reference to the image or its buffer held, and extra
+ care should be taken for modes with more than 8 bits per
+ component: the data is stored in native byte order and it can be
+ **not** aligned on 2 or 4 byte boundaries.
+
+
+Examples
+========
+
+A few examples of common operations with the new ``Image`` class and
+protocol::
+
+ # create a new black RGB image of 6x9 pixels
+ rgb_image = imageop.Image(imageop.RGB, (6, 9))
+
+ # same as above, but initialize the image to bright red
+ rgb_image = imageop.Image(imageop.RGB, (6, 9), color=(255, 0, 0))
+
+ # convert the image to YCbCr
+ yuv_image = imageop.Image(imageop.JPEG_YV12, source=rgb_image)
+
+ # read the value of a pixel and split it into three ints
+ r, g, b = rgb_image[x, y]
+
+ # modify the magenta component of a pixel in a CMYK image
+ cmyk_image[x, y].m = 13
+
+ # modify the Y (luma) component of a pixel in a *YV12 image and
+ # its corresponding subsampled Cr (red chroma)
+ yuv_image.y[x, y] = 42
+ yuv_image.cr[x // 2, y // 2] = 54
+
+ # iterate over an image
+ for line in rgb_image:
+ for pixel in line:
+ # swap red and blue, and set green to 0
+ pixel.value = pixel.b, 0, pixel.r
+
+ # find the maximum value of the red component in the image
+ max_red = max(pixel.r for pixel in rgb_image.pixels())
+
+ # count the number of colors in the image
+ num_of_colors = len(set(tuple(pixel) for pixel in image.pixels()))
+
+ # copy a block of 4x2 pixels near the upper right corner of an
+ # image and paste it into the lower left corner of the same image
+ image[:4, -2:] = image[-6:-2, 1:3]
+
+ # create a copy of the image, except that the new image can have a
+ # different (usually empty) info dict
+ new_image = image[:]
+
+ # create a mirrored copy of the image, with the left and right
+ # sides flipped
+ flipped_image = image[::-1, :]
+
+ # downsample an image to half its original size using a fast, low
+ # quality operation and a slower, high quality one:
+ low_quality_image = image[::2, ::2]
+ new_size = image.size.width // 2, image.size.height // 2
+ high_quality_image = imageop.Image(size=new_size, source=image)
+
+ # direct buffer access
+ rgb_image[0, 0] = r, g, b
+ assert tuple(rgb_image.buffer[:3]) == (r, g, b)
+
+
+Backwards Compatibility
+=======================
+
+There are three areas touched by this PEP where backwards
+compatibility should be considered:
+
+* **Python 2.6**: new classes and objects are added to the ``imageop``
+ module without touching the existing module contents; new methods
+ and attributes will be added to ``Tkinter.PhotoImage`` and its
+ ``__getitem__`` and ``__setitem__`` methods will be modified to
+ accept integers, tuples and slices (currently they only accept
+ strings). All the changes provide a superset of the existing
+ functionality, so no major compatibility issues are expected.
+
+* **Python 3.0**: the legacy contents of the ``imageop`` module will
+ be deleted, according to PEP 3108; everything defined in this
+ proposal will work like in Python 2.x with the exception of the
+ usual 2.x/3.0 differences (e.g. support for ``long`` integers and
+ for interpreting ``str`` instances as sequences of bytes will be
+ dropped).
+
+* **external libraries**: the names and the semantics of the standard
+ image methods and attributes are carefully chosen to allow some
+ external libraries that manipulate images (including at least PIL,
+ wxPython and pygame) to implement the new protocol in their image
+ classes without breaking compatibility with existing code. The only
+ blatant conflicts between the image protocol and NumPy arrays are
+ the value of the ``size`` attribute and the coordinates order in the
+ ``image[x, y]`` expression.
+
+
+Reference Implementation
+========================
+
+If this PEP is accepted, the author will provide a reference
+implementation of the new classes in pure Python (that can run in
+CPython, PyPy, Jython and IronPython) and a second one optimized for
+speed in Python and C, suitable for inclusion in the CPython standard
+library. The author will also submit the required Tkinter patches.
+For all the code will be available a version for Python 2.x and a
+version for Python 3.0 (it is expected that the two version will be
+very similar and the Python 3.0 one will probably be generated almost
+completely automatically).
+
+
+Acknowledgments
+===============
+
+The implementation of this PEP, if accepted, is sponsored by Google
+through the Google Summer of Code program.
+
+
+Copyright
+=========
+
+This document has been placed in the public domain.
+
+
+
+..
+ Local Variables:
+ mode: indented-text
+ indent-tabs-mode: nil
+ sentence-end-double-space: t
+ fill-column: 70
+ coding: utf-8
+ End: