357 lines
13 KiB
Python
357 lines
13 KiB
Python
|
## pygame - Python Game Library
|
||
|
## 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
|
||
|
##
|
||
|
## Marcus von Appen
|
||
|
## mva@sysfault.org
|
||
|
|
||
|
"""pygame module for accessing surface pixel data using numpy
|
||
|
|
||
|
Functions to convert pixel data between pygame Surfaces and Numpy
|
||
|
arrays. This module will only be available when pygame can use the
|
||
|
external Numpy package.
|
||
|
|
||
|
Note, that numpyarray is an optional module. It requires that Numpy is
|
||
|
installed to be used. If not installed, an exception will be raised when
|
||
|
it is used. eg. ImportError: no module named numpy
|
||
|
|
||
|
Every pixel is stored as a single integer value to represent the red,
|
||
|
green, and blue colors. The 8bit images use a value that looks into a
|
||
|
colormap. Pixels with higher depth use a bit packing process to place
|
||
|
three or four values into a single number.
|
||
|
|
||
|
The Numpy arrays are indexed by the X axis first, followed by the Y
|
||
|
axis. Arrays that treat the pixels as a single integer are referred to
|
||
|
as 2D arrays. This module can also separate the red, green, and blue
|
||
|
color values into separate indices. These types of arrays are referred
|
||
|
to as 3D arrays, and the last index is 0 for red, 1 for green, and 2 for
|
||
|
blue.
|
||
|
|
||
|
In contrast to Numeric Numpy does use unsigned 16bit integers, images
|
||
|
with 16bit data will be treated as unsigned integers.
|
||
|
"""
|
||
|
|
||
|
import pygame
|
||
|
from pygame.compat import bytes_
|
||
|
from pygame.pixelcopy import array_to_surface, surface_to_array, \
|
||
|
map_array as pix_map_array, make_surface as pix_make_surface
|
||
|
import numpy
|
||
|
from numpy import array as numpy_array, empty as numpy_empty, \
|
||
|
around as numpy_around, uint32 as numpy_uint32, \
|
||
|
ndarray as numpy_ndarray
|
||
|
|
||
|
#float96 not available on all numpy versions.
|
||
|
numpy_floats = []
|
||
|
for type_name in "float float32 float64 float96".split():
|
||
|
if hasattr(numpy, type_name):
|
||
|
numpy_floats.append(getattr(numpy, type_name))
|
||
|
|
||
|
# Pixel sizes corresponding to NumPy supported integer sizes, and therefore
|
||
|
# permissible for 2D reference arrays.
|
||
|
_pixel2d_bitdepths = set([8, 16, 32])
|
||
|
|
||
|
|
||
|
def blit_array (surface, array):
|
||
|
"""pygame.surfarray.blit_array(Surface, array): return None
|
||
|
|
||
|
Blit directly from a array values.
|
||
|
|
||
|
Directly copy values from an array into a Surface. This is faster than
|
||
|
converting the array into a Surface and blitting. The array must be the
|
||
|
same dimensions as the Surface and will completely replace all pixel
|
||
|
values. Only integer, ascii character and record arrays are accepted.
|
||
|
|
||
|
This function will temporarily lock the Surface as the new values are
|
||
|
copied.
|
||
|
"""
|
||
|
if isinstance(array, numpy_ndarray) and array.dtype in numpy_floats:
|
||
|
array = array.round(0).astype(numpy_uint32)
|
||
|
return array_to_surface(surface, array)
|
||
|
|
||
|
def make_surface(array):
|
||
|
"""pygame.surfarray.make_surface (array): return Surface
|
||
|
|
||
|
Copy an array to a new surface.
|
||
|
|
||
|
Create a new Surface that best resembles the data and format on the
|
||
|
array. The array can be 2D or 3D with any sized integer values.
|
||
|
"""
|
||
|
if isinstance(array, numpy_ndarray) and array.dtype in numpy_floats:
|
||
|
array = array.round(0).astype(numpy_uint32)
|
||
|
return pix_make_surface (array)
|
||
|
|
||
|
def array2d(surface):
|
||
|
"""pygame.numpyarray.array2d(Surface): return array
|
||
|
|
||
|
copy pixels into a 2d array
|
||
|
|
||
|
Copy the pixels from a Surface into a 2D array. The bit depth of the
|
||
|
surface will control the size of the integer values, and will work
|
||
|
for any type of pixel format.
|
||
|
|
||
|
This function will temporarily lock the Surface as pixels are copied
|
||
|
(see the Surface.lock - lock the Surface memory for pixel access
|
||
|
method).
|
||
|
"""
|
||
|
bpp = surface.get_bytesize()
|
||
|
try:
|
||
|
dtype = (numpy.uint8, numpy.uint16, numpy.int32, numpy.int32)[bpp - 1]
|
||
|
except IndexError:
|
||
|
raise ValueError("unsupported bit depth %i for 2D array" % (bpp * 8,))
|
||
|
size = surface.get_size()
|
||
|
array = numpy.empty(size, dtype)
|
||
|
surface_to_array(array, surface)
|
||
|
return array
|
||
|
|
||
|
def pixels2d(surface):
|
||
|
"""pygame.numpyarray.pixels2d(Surface): return array
|
||
|
|
||
|
reference pixels into a 2d array
|
||
|
|
||
|
Create a new 2D array that directly references the pixel values in a
|
||
|
Surface. Any changes to the array will affect the pixels in the
|
||
|
Surface. This is a fast operation since no data is copied.
|
||
|
|
||
|
Pixels from a 24-bit Surface cannot be referenced, but all other
|
||
|
Surface bit depths can.
|
||
|
|
||
|
The Surface this references will remain locked for the lifetime of
|
||
|
the array (see the Surface.lock - lock the Surface memory for pixel
|
||
|
access method).
|
||
|
"""
|
||
|
if (surface.get_bitsize() not in _pixel2d_bitdepths):
|
||
|
raise ValueError("unsupport bit depth for 2D reference array")
|
||
|
try:
|
||
|
return numpy_array(surface.get_view('2'), copy=False)
|
||
|
except (ValueError, TypeError):
|
||
|
raise ValueError("bit depth %i unsupported for 2D reference array" %
|
||
|
(surface.get_bitsize(),))
|
||
|
|
||
|
def array3d(surface):
|
||
|
"""pygame.numpyarray.array3d(Surface): return array
|
||
|
|
||
|
copy pixels into a 3d array
|
||
|
|
||
|
Copy the pixels from a Surface into a 3D array. The bit depth of the
|
||
|
surface will control the size of the integer values, and will work
|
||
|
for any type of pixel format.
|
||
|
|
||
|
This function will temporarily lock the Surface as pixels are copied
|
||
|
(see the Surface.lock - lock the Surface memory for pixel access
|
||
|
method).
|
||
|
"""
|
||
|
w, h = surface.get_size()
|
||
|
array = numpy.empty((w, h, 3), numpy.uint8)
|
||
|
surface_to_array(array, surface)
|
||
|
return array
|
||
|
|
||
|
def pixels3d (surface):
|
||
|
"""pygame.numpyarray.pixels3d(Surface): return array
|
||
|
|
||
|
reference pixels into a 3d array
|
||
|
|
||
|
Create a new 3D array that directly references the pixel values in a
|
||
|
Surface. Any changes to the array will affect the pixels in the
|
||
|
Surface. This is a fast operation since no data is copied.
|
||
|
|
||
|
This will only work on Surfaces that have 24-bit or 32-bit
|
||
|
formats. Lower pixel formats cannot be referenced.
|
||
|
|
||
|
The Surface this references will remain locked for the lifetime of
|
||
|
the array (see the Surface.lock - lock the Surface memory for pixel
|
||
|
access method).
|
||
|
"""
|
||
|
return numpy_array(surface.get_view('3'), copy=False)
|
||
|
|
||
|
def array_alpha(surface):
|
||
|
"""pygame.numpyarray.array_alpha(Surface): return array
|
||
|
|
||
|
copy pixel alphas into a 2d array
|
||
|
|
||
|
Copy the pixel alpha values (degree of transparency) from a Surface
|
||
|
into a 2D array. This will work for any type of Surface
|
||
|
format. Surfaces without a pixel alpha will return an array with all
|
||
|
opaque values.
|
||
|
|
||
|
This function will temporarily lock the Surface as pixels are copied
|
||
|
(see the Surface.lock - lock the Surface memory for pixel access
|
||
|
method).
|
||
|
"""
|
||
|
size = surface.get_size()
|
||
|
array = numpy.empty(size, numpy.uint8)
|
||
|
surface_to_array(array, surface, 'A')
|
||
|
return array
|
||
|
|
||
|
def pixels_alpha(surface):
|
||
|
"""pygame.numpyarray.pixels_alpha(Surface): return array
|
||
|
|
||
|
reference pixel alphas into a 2d array
|
||
|
|
||
|
Create a new 2D array that directly references the alpha values
|
||
|
(degree of transparency) in a Surface. Any changes to the array will
|
||
|
affect the pixels in the Surface. This is a fast operation since no
|
||
|
data is copied.
|
||
|
|
||
|
This can only work on 32-bit Surfaces with a per-pixel alpha value.
|
||
|
|
||
|
The Surface this array references will remain locked for the
|
||
|
lifetime of the array.
|
||
|
"""
|
||
|
return numpy.array(surface.get_view('A'), copy=False)
|
||
|
|
||
|
def pixels_red(surface):
|
||
|
"""pygame.surfarray.pixels_red(Surface): return array
|
||
|
|
||
|
Reference pixel red into a 2d array.
|
||
|
|
||
|
Create a new 2D array that directly references the red values
|
||
|
in a Surface. Any changes to the array will affect the pixels
|
||
|
in the Surface. This is a fast operation since no data is copied.
|
||
|
|
||
|
This can only work on 24-bit or 32-bit Surfaces.
|
||
|
|
||
|
The Surface this array references will remain locked for the
|
||
|
lifetime of the array.
|
||
|
"""
|
||
|
return numpy.array(surface.get_view('R'), copy=False)
|
||
|
|
||
|
def array_red(surface):
|
||
|
"""pygame.numpyarray.array_red(Surface): return array
|
||
|
|
||
|
copy pixel red into a 2d array
|
||
|
|
||
|
Copy the pixel red values from a Surface into a 2D array. This will work
|
||
|
for any type of Surface format.
|
||
|
|
||
|
This function will temporarily lock the Surface as pixels are copied
|
||
|
(see the Surface.lock - lock the Surface memory for pixel access
|
||
|
method).
|
||
|
"""
|
||
|
size = surface.get_size()
|
||
|
array = numpy.empty(size, numpy.uint8)
|
||
|
surface_to_array(array, surface, 'R')
|
||
|
return array
|
||
|
|
||
|
def pixels_green(surface):
|
||
|
"""pygame.surfarray.pixels_green(Surface): return array
|
||
|
|
||
|
Reference pixel green into a 2d array.
|
||
|
|
||
|
Create a new 2D array that directly references the green values
|
||
|
in a Surface. Any changes to the array will affect the pixels
|
||
|
in the Surface. This is a fast operation since no data is copied.
|
||
|
|
||
|
This can only work on 24-bit or 32-bit Surfaces.
|
||
|
|
||
|
The Surface this array references will remain locked for the
|
||
|
lifetime of the array.
|
||
|
"""
|
||
|
return numpy.array(surface.get_view('G'), copy=False)
|
||
|
|
||
|
def array_green(surface):
|
||
|
"""pygame.numpyarray.array_green(Surface): return array
|
||
|
|
||
|
copy pixel green into a 2d array
|
||
|
|
||
|
Copy the pixel green values from a Surface into a 2D array. This will work
|
||
|
for any type of Surface format.
|
||
|
|
||
|
This function will temporarily lock the Surface as pixels are copied
|
||
|
(see the Surface.lock - lock the Surface memory for pixel access
|
||
|
method).
|
||
|
"""
|
||
|
size = surface.get_size()
|
||
|
array = numpy.empty(size, numpy.uint8)
|
||
|
surface_to_array(array, surface, 'G')
|
||
|
return array
|
||
|
|
||
|
def pixels_blue (surface):
|
||
|
"""pygame.surfarray.pixels_blue(Surface): return array
|
||
|
|
||
|
Reference pixel blue into a 2d array.
|
||
|
|
||
|
Create a new 2D array that directly references the blue values
|
||
|
in a Surface. Any changes to the array will affect the pixels
|
||
|
in the Surface. This is a fast operation since no data is copied.
|
||
|
|
||
|
This can only work on 24-bit or 32-bit Surfaces.
|
||
|
|
||
|
The Surface this array references will remain locked for the
|
||
|
lifetime of the array.
|
||
|
"""
|
||
|
return numpy.array(surface.get_view('B'), copy=False)
|
||
|
|
||
|
def array_blue(surface):
|
||
|
"""pygame.numpyarray.array_blue(Surface): return array
|
||
|
|
||
|
copy pixel blue into a 2d array
|
||
|
|
||
|
Copy the pixel blue values from a Surface into a 2D array. This will work
|
||
|
for any type of Surface format.
|
||
|
|
||
|
This function will temporarily lock the Surface as pixels are copied
|
||
|
(see the Surface.lock - lock the Surface memory for pixel access
|
||
|
method).
|
||
|
"""
|
||
|
size = surface.get_size()
|
||
|
array = numpy.empty(size, numpy.uint8)
|
||
|
surface_to_array(array, surface, 'B')
|
||
|
return array
|
||
|
|
||
|
def array_colorkey(surface):
|
||
|
"""pygame.numpyarray.array_colorkey(Surface): return array
|
||
|
|
||
|
copy the colorkey values into a 2d array
|
||
|
|
||
|
Create a new array with the colorkey transparency value from each
|
||
|
pixel. If the pixel matches the colorkey it will be fully
|
||
|
tranparent; otherwise it will be fully opaque.
|
||
|
|
||
|
This will work on any type of Surface format. If the image has no
|
||
|
colorkey a solid opaque array will be returned.
|
||
|
|
||
|
This function will temporarily lock the Surface as pixels are
|
||
|
copied.
|
||
|
"""
|
||
|
size = surface.get_size()
|
||
|
array = numpy.empty(size, numpy.uint8)
|
||
|
surface_to_array(array, surface, 'C')
|
||
|
return array
|
||
|
|
||
|
def map_array(surface, array):
|
||
|
"""pygame.numpyarray.map_array(Surface, array3d): return array2d
|
||
|
|
||
|
map a 3d array into a 2d array
|
||
|
|
||
|
Convert a 3D array into a 2D array. This will use the given Surface
|
||
|
format to control the conversion.
|
||
|
|
||
|
Note: arrays do not need to be 3D, as long as the minor axis has
|
||
|
three elements giving the component colours, any array shape can be
|
||
|
used (for example, a single colour can be mapped, or an array of
|
||
|
colours). The array shape is limited to eleven dimensions maximum,
|
||
|
including the three element minor axis.
|
||
|
"""
|
||
|
if array.ndim == 0:
|
||
|
raise ValueError("array must have at least 1 dimension")
|
||
|
shape = array.shape
|
||
|
if shape[-1] != 3:
|
||
|
raise ValueError("array must be a 3d array of 3-value color data")
|
||
|
target = numpy_empty(shape[:-1], numpy.int32)
|
||
|
pix_map_array(target, array, surface)
|
||
|
return target
|
||
|
|