diff --git a/docs/dev/api.rst b/docs/dev/api.rst new file mode 100644 index 0000000..30181f2 --- /dev/null +++ b/docs/dev/api.rst @@ -0,0 +1,741 @@ +############ +Internal API +############ + + +.. module:: pymaging.image + +********************* +:mod:`pymaging.image` +********************* + + +.. class:: Image(pixelarray, mode, palette=None) + + The image class. This is the core class of pymaging. + + :param pixelarray: An instance of :class:`pymaging.pixelarray.GenericPixelArray` or a subclass of it. + :param mode: The color mode. A :class:`pymaging.color.ColorType` instance. + :param palette: If given, this will be the color palette used by the image. A list of :class:`pymaging.color.Color` + instances. + + .. attribute:: mode + + The color mode used in this image. A :class:`pymaging.color.ColorType` instance. + + .. attribute:: palette + + The palette used in this image. A list of :class:`pymaging.color.Color` instances or ``None``. + + .. attribute:: reverse_palette + + Cache for :meth:`get_reverse_palette`. + + .. attribute:: pixels + + The :class:`pymaging.pixelarray.GenericPixelArray` (or subclass thereof) instance holding the image data. + + .. attribute:: width + + The width of the image (in pixels) + + .. attribute:: height + + The height of the image (in pixels) + + .. attribute:: pixelsize + + The size of a pixel (in :attr:`pixels`). ``1`` usually indicates an image with a palette. ``3`` is an standard + RGB image. ``4`` is a RGBA image. + + .. classmethod:: open(fileobj) + + Creates a new image from a file object + + :param fileobj: A file like object open for reading. + + .. classmethod:: open_from_path(filepath) + + Creates a new image from a file path + + :param fileobj: A string pointing at a image file. + + .. method:: save(fileobj, format) + + Saves the image. + + :param fileobj: A file-like object (opened for writing) to which the image should be saved. + :param format: The format to use for saving (as a string). + + .. method:: save_to_path(filepath, format=None): + + Saves the image to a path. + + :param filepath: A string pointing at a (writable) file location where the image should be saved. + :param format: If given, the format (string) to use for saving. If ``None``, the format will be guessed from + the file extension used in ``filepath``. + + .. method:: get_reverse_palette + + Returns :attr:`reverse_palette`. If :attr:`reverse_palette` is ``None``, calls :meth:`_fill_reverse_palette`. + The reverse palette is a dictionary. If the image has no palette, an empty dictionary is returned. + + .. method:: _fill_reverse_palette + + Populates the reverse palette, which is a mapping of :class:`pymaging.color.Color` instances to their index in + the palette. Sets :attr:`reverse_palette`. + + .. method:: resize(width, height, resample_algorithm=nearest, resize_canvas=True) + + Resizes the image to the given ``width`` and ``height``, using given ``resample_algorithm``. If + ``resize_canvas`` is ``False``, the actual image dimensions do not change, in which case the excess pixels will + be filled by a background color (usually black). Returns the resized copy of this image. + + :param width: The new width as integer in pixels. + :param height: The new height as integer in pixels. + :param resample_algorithm: The resample algorithm to use. Should be a :class:`pymaging.resample.Resampler` + instance. + :param resize_canvas: Boolean flag whether to resize the canvas or not. + + .. method:: affine(transform, resample_algorithm=nearest, resize_canvas=True) + + Advanced version of :meth:`resize`. Instead of a ``height`` and ``width``, a + :class:`pymaging.affine.AffineTransform` is passed according to which the image is transformed. + Returns the transformed copy of the image. + + .. method:: rotate(degrees, clockwise=False, resample_algorithm=nearest, resize_canvas=True) + + Rotates the image by ``degrees`` degrees counter-clockwise (unless ``clockwise`` is ``True``). Interpolation of + the pixels is done using ``resample_algorithm``. Returns the rotated copy of this image. + + .. method:: get_pixel(x, y) + + Returns the pixel at the given ``x``/``y`` location. If the pixel is outside the image, raises an + :exc:`IndexError`. If the image has a palette, the palette lookup will be performed by this method. The pixel is + returned as a list if integers. + + .. method:: get_color(x, y) + + Same as :meth:`get_pixel` but returns a :class:`pymaging.colors.Color` instance. + + .. method:: set_color(x, y, color) + + The core drawing API. This should be used to draw pixels to the image. Sets the pixel at ``x``/``y`` to the + color given. The color should be a :class:`pymaging.colors.Color` instance. If the image has a palette, only + colors that are in the palette are supported. + + .. method:: flip_top_bottom + + Vertically flips the image and returns the flipped copy. + + .. method:: flip_left_right + + Horizontally flips the image and returns the flipped copy. + + .. method:: crop(width, height, padding_top, padding_left) + + Crops the pixel to the new ``width`` and ``height``, starting the cropping at the offset given with + ``padding_top`` and ``padding_left``. Returns the cropped copy of this image. + + .. method:: draw(shape, color) + + Draws the shape using the given color to this image. The shape should be a :class:`pymaging.shapes.BaseShape` + subclass instance, or any object that has a ``iter_pixels`` method, which when called with a + :class:`pymaging.colors.Color` instance, returns an iterator that yields tuples of ``(x, y, color)`` of colors + to be drawn to pixels. + + This method is just a shortcut around :meth:`set_color` which allows users to write shape classes that do the + heavy lifting for them. + + This method operates **in place** and does not return a copy of this image! + + +.. module:: pymaging.affine + +********************** +:mod:`pymaging.affine` +********************** + + +.. class:: AffineTransform(matrix) + + Affine transformation matrix. Used by :meth:`pymaging.image.Image.affine`. + + The matrix should be given either as a sequence of 9 values or a sequence of 3 sequences of 3 values. + + .. TODO:: Needs documentation about the actual values of the matrix. + + .. attribute:: matrix + + .. TODO:: Needs documentation. + + .. method:: _determinant + + .. TODO:: Needs documentation. + + .. method:: inverse + + .. TODO:: Needs documentation. + + .. method:: rotate(degrees, clockwise=False) + + .. TODO:: Needs documentation. + + .. method:: scale(x_factor, y_factor=None) + + .. TODO:: Needs documentation. + + .. method:: translate(dx, dy) + + .. TODO:: Needs documentation. + + +.. module:: pymaging.colors + +********************** +:mod:`pymaging.colors` +********************** + +.. function:: _mixin_alpha(colors, alpha) + + Applies the given alpha value to all colors. Colors should be a list of three items: ``r``, ``g`` and ``b``. + + +.. class:: Color(red, green, blue alpha) + + Represents a color. All four parameters should be integers between 0 and 255. + + .. attribute:: red + .. attribute:: green + .. attribute:: blue + .. attribute:: alpha + + .. classmethod:: from_pixel(pixel) + + Given a pixel (a list of colors), create a :class:`Color` instance. + + .. classmethod:: from_hexcode(hexcode) + + Given a hexcode (a string of 3, 4, 6 or 8 characters, optionally prefixed by ``'#'``), construct a + :class:`Color` instance. + + .. method:: get_for_brightness(brightness) + + Given a brightness (alpha value) between 0 and 1, return the current color for that brightness. + + .. method:: cover_with(cover_color) + + Covers the current color with another color respecting their respective alpha values. If the ``cover_color`` + is a solid color, return a copy of the ``cover_color``. ``cover_color`` must be an instance of :class:`Color`. + + .. method:: to_pixel(pixelsize) + + Returns this color as a pixel (list of integers) for the given ``pixelsize`` (3 or 4). + + .. method:: to_hexcode + + Returns this color as RGBA hexcode. (Without leading ``'#'``). + + +.. class:: ColorType + + A named tuple holding the length of a color type (pixelsize) and whether this color type supports the alpha channel + or not. + + .. attribute:: length + .. attribute:: alpha + + +.. data:: RGB + + RGB :class:`ColorType`. + +.. data:: RGBA + + RGBA :class:`ColorType`. + + +.. module:: pymaging.exceptions + +************************* +:mod:`pymaging.exception` +************************* + + +.. exception:: PymagingExcpetion + + The root exception type for all exceptions defined in this module. + +.. exception:: FormatNotSupported + + Raised if an image is saved or loaded in a format not supported by pymaging. + +.. exception:: InvalidColor + + Raised if an invalid color is used on an image (usually when the image has a palette). + + +.. module:: pymaging.formats + +*********************** +:mod:`pymaging.formats` +*********************** + +Loads and maintains the formats supported in this installation. + +.. class:: Format(decode, encode, extensions) + + A named tuple that should be used to define formats for pymaging. ``decode`` and ``encode`` are callables that + decode and encode an image in this format. ``extensions`` is a list of file extensions this image type could have. + + .. attribute:: decode + .. attribute:: encode + .. attribute:: extensions + +.. class:: FormatRegistry + + A singleton class for format registration + + .. method:: _populate + + Populates the registry using package resources. + + .. method:: register(format) + + Manually registers a format, which must be an instance of :class:`Format`. + + .. method:: get_format_objects + + Returns all formats in this registry. + + .. method:: get_format(format) + + Given a format name (eg file extension), returns the :class:`Format` instance if it's registered, otherwise + ``None``. + +.. data:: registry + + The singleton instance of :class:`FormatRegistry`. + +.. function:: get_format_objects + + Shortcut to :data:`registry.get_format_objects`. + +.. function:: get_format + + Shortcut to :data:`registry.get_format`. + +.. function:: register + + Shortcut to :data:`registry.register`. + + +.. module:: pymaging.helpers + +*********************** +:mod:`pymaging.helpers` +*********************** + + +.. function:: get_transformed_dimensions(transform, box) + + Takes an affine transform and a four-tuple of (x0, y0, x1, y1) coordinates. Transforms each corner of the given box, + and returns the (width, height) of the transformed box. + + +.. module:: pymaging.pixelarray + +************************** +:mod:`pymaging.pixelarray` +************************** + + +.. class:: GenericPixelArray(data, width, height, pixelsize) + + The base pixel array class. ``data`` should be a flat :class:`array.array` instance of pixel data, ``width`` and + ``height`` are the dimensions of the array and ``pixelsize`` defines how many items in the ``data`` array define a + single pixel. + + Use :func:`get_pixel_array` to instantiate this class! + + .. attribute:: data + + The image data as array. + + .. attribute:: width + + The width of the pixel array. + + .. attribute:: height + + The height of the pixel array. + + .. attribute:: pixelsize + + The size of a single pixel + + .. attribute:: line_length + + The length of a line. (:attr:`width` multiplied with :attr:`pixelsize`). + + .. attribute:: size + + The size of the pixel array. + + .. method:: _precalculate + + Precalculates :attr:`line_width` and :attr:`size`. Should be called whenever :attr:`width`, :attr:`height` or + :attr:`pixelsize` change. + + .. method:: _translate(x, y) + + Translates the logical ``x``/``y`` coordinates into the start of the pixel in the pixel array. + + .. method:: get(x, y) + + Returns the pixel at ``x``/``y`` as list of integers. + + .. method:: set(x, y, pixel) + + Sets the ``pixel`` to ``x``/``y``. + + .. method:: copy_flipped_top_bottom + + Returns a copy of this pixel array with the lines flipped from top to bottom. + + .. method:: copy_flipped_left_right + + Returns a copy of this pixel array with the lines flipped from left to right. + + .. method:: copy + + Returns a copy of this pixel array. + + .. method:: remove_lines(offset, amount) + + Removes ``amount`` lines from this pixel array after ``offset`` (from the top). + + .. method:: remove_columns(offset, amount) + + Removes ``amount`` columns from this pixel array after ``offset`` (from the left). + + .. note:: + + If :meth:`remove_columns` and :meth:`remove_lines` are used together, :meth:`remove_lines` should always be + called first, as that method is a lot faster and :meth:`remove_columns` gets faster the fewer lines there + are in a pixel array. + + .. method:: add_lines(offset, amount, fill=0) + + Adds ``amount`` lines to the pixel array after ``offset`` (from the top) and fills it with ``fill``. + + .. method:: add_columns(offset, amount, fill=0) + + Adds ``amount`` columns to the pixel array after ``offset`` (from the left) and fill it with ``fill``. + + .. note:: + + As with :meth:`remove_columns`, the cost of this method grows with the amount of lines in the pixe array. + If it is used together with :meth:`add_lines`, :meth:`add_columns` should be called first. + + +.. class:: PixelArray1(data, width, height) + + Subclass of :class:`GenericPixelArray`, optimized for pixelsize 1. + + Use :func:`get_pixel_array` to instantiate this class! + +.. class:: PixelArray2(data, width, height) + + Subclass of :class:`GenericPixelArray`, optimized for pixelsize 2. + + Use :func:`get_pixel_array` to instantiate this class! + + +.. class:: PixelArray3(data, width, height) + + Subclass of :class:`GenericPixelArray`, optimized for pixelsize 3. + + Use :func:`get_pixel_array` to instantiate this class! + + +.. class:: PixelArray4(data, width, height) + + Subclass of :class:`GenericPixelArray`, optimized for pixelsize 4. + + Use :func:`get_pixel_array` to instantiate this class! + + +.. function:: get_pixel_array(data, width, height, pixelsize) + + Returns the most optimal pixel array class for the given pixelsize. Use this function instead of instantating the + pixel array classes directly. + + +.. module:: pymaging.resample + +************************ +:mod:`pymaging.resample` +************************ + + +.. class:: Resampler + + Base class for resampler algorithms. Should never be instantated directly. + + .. method:: affine(source, transform, resize_canvas=True) + + .. TODO:: Document. + + .. method:: resize(source, width, height, resize_canvas=True) + + .. TODO:: Document. + + +.. class:: Nearest + + Subclass of :class:`Resampler`. Implements the nearest neighbor resampling algorithm which is very fast but creates + very ugly resampling artifacts. + + +.. class:: Bilinear + + Subclass of :class:`Resampler` implementing the bilinear resampling algorithm, which produces much nicer results at + the cost of computation time. + + +.. data:: nearest + + Singleton instance of the :class:`Nearest` resampler. + + +.. data:: bilinear + + Singleton instance of the :class:`Bilinear` resampler. + + +.. module:: pymaging.shapes + +********************** +:mod:`pymaging.shapes` +********************** + + +Shapes are the high level drawing API used by :meth:`pymaging.image.Image.draw`. + + +.. class:: BaseShape + + Dummy base class for shapes. + + .. method:: iter_pixels(color) + + In subclasses, this is the API used by :meth:`pymaging.image.Image.draw` to draw to an image. Should return an + iterator that yields ``x``, ``y``, ``color`` tuples. + + +.. class:: Pixel(x, y) + + A simple single-pixel drawing object. + + +.. class:: Line(start_x, start_y, end_x, end_y) + + Simple line drawing algorithm using the Bresenham Line Algorithm. Draws non-anti-aliased lines, which is very fast + but for lines that are not exactly horizontal or vertical, this produces rather ugly lines. + + +.. class:: AntiAliasedLine(start_x, start_y, end_x, end_y) + + Draws an anti-aliased line using Xiaolin Wu's line algorithm. This has a lot higher computation costs than + :class:`Line` but produces much nicer results. When used on an image with a palette, this shape might cause errors. + + +.. module:: pymaging.test_utils + +************************** +:mod:`pymaging.test_utils` +************************** + + +.. function:: image_factory(colors, alpha=True) + + Creates an image given a list of lists of :class:`pymaging.color.Color` instances. The ``alpha`` parameter defines + the pixel size of the image. + + +.. class:: PymagingBaseTestCase + + .. method:: assertImage(image, colors, alpha=True) + + Checks that an image is the same as the dummy image given. ``colors`` and ``alpha`` are passed to + :func:`image_factory` to create a comparison image. + + +.. module:: pymaging.utils + +********************* +:mod:`pymaging.utils` +********************* + + +.. function:: fdiv(a, b) + + Does a float division of ``a`` and ``b`` regardless of their type and returns a float. + + +.. function:: get_test_file(testfile, fname) + + Returns the full path to a file for a given test. + + +.. module:: pymaging.webcolors + +************************* +:mod:`pymaging.webcolors` +************************* + + +Defines constant :class:`pymaging.color.Color` instances for web colors. + +.. data:: IndianRed +.. data:: LightCoral +.. data:: Salmon +.. data:: DarkSalmon +.. data:: LightSalmon +.. data:: Red +.. data:: Crimson +.. data:: FireBrick +.. data:: DarkRed +.. data:: Pink +.. data:: LightPink +.. data:: HotPink +.. data:: DeepPink +.. data:: MediumVioletRed +.. data:: PaleVioletRed +.. data:: LightSalmon +.. data:: Coral +.. data:: Tomato +.. data:: OrangeRed +.. data:: DarkOrange +.. data:: Orange +.. data:: Gold +.. data:: Yellow +.. data:: LightYellow +.. data:: LemonChiffon +.. data:: LightGoldenrodYellow +.. data:: PapayaWhip +.. data:: Moccasin +.. data:: PeachPuff +.. data:: PaleGoldenrod +.. data:: Khaki +.. data:: DarkKhaki +.. data:: Lavender +.. data:: Thistle +.. data:: Plum +.. data:: Violet +.. data:: Orchid +.. data:: Fuchsia +.. data:: Magenta +.. data:: MediumOrchid +.. data:: MediumPurple +.. data:: BlueViolet +.. data:: DarkViolet +.. data:: DarkOrchid +.. data:: DarkMagenta +.. data:: Purple +.. data:: Indigo +.. data:: DarkSlateBlue +.. data:: SlateBlue +.. data:: MediumSlateBlue +.. data:: GreenYellow +.. data:: Chartreuse +.. data:: LawnGreen +.. data:: Lime +.. data:: LimeGreen +.. data:: PaleGreen +.. data:: LightGreen +.. data:: MediumSpringGreen +.. data:: SpringGreen +.. data:: MediumSeaGreen +.. data:: SeaGreen +.. data:: ForestGreen +.. data:: Green +.. data:: DarkGreen +.. data:: YellowGreen +.. data:: OliveDrab +.. data:: Olive +.. data:: DarkOliveGreen +.. data:: MediumAquamarine +.. data:: DarkSeaGreen +.. data:: LightSeaGreen +.. data:: DarkCyan +.. data:: Teal +.. data:: Aqua +.. data:: Cyan +.. data:: LightCyan +.. data:: PaleTurquoise +.. data:: Aquamarine +.. data:: Turquoise +.. data:: MediumTurquoise +.. data:: DarkTurquoise +.. data:: CadetBlue +.. data:: SteelBlue +.. data:: LightSteelBlue +.. data:: PowderBlue +.. data:: LightBlue +.. data:: SkyBlue +.. data:: LightSkyBlue +.. data:: DeepSkyBlue +.. data:: DodgerBlue +.. data:: CornflowerBlue +.. data:: RoyalBlue +.. data:: Blue +.. data:: MediumBlue +.. data:: DarkBlue +.. data:: Navy +.. data:: MidnightBlue +.. data:: Cornsilk +.. data:: BlanchedAlmond +.. data:: Bisque +.. data:: NavajoWhite +.. data:: Wheat +.. data:: BurlyWood +.. data:: Tan +.. data:: RosyBrown +.. data:: SandyBrown +.. data:: Goldenrod +.. data:: DarkGoldenrod +.. data:: Peru +.. data:: Chocolate +.. data:: SaddleBrown +.. data:: Sienna +.. data:: Brown +.. data:: Maroon +.. data:: White +.. data:: Snow +.. data:: Honeydew +.. data:: MintCream +.. data:: Azure +.. data:: AliceBlue +.. data:: GhostWhite +.. data:: WhiteSmoke +.. data:: Seashell +.. data:: Beige +.. data:: OldLace +.. data:: FloralWhite +.. data:: Ivory +.. data:: AntiqueWhite +.. data:: Linen +.. data:: LavenderBlush +.. data:: MistyRose +.. data:: Gainsboro +.. data:: LightGrey +.. data:: Silver +.. data:: DarkGray +.. data:: Gray +.. data:: DimGray +.. data:: LightSlateGray +.. data:: SlateGray +.. data:: DarkSlateGray +.. data:: Black diff --git a/docs/dev/image.rst b/docs/dev/image.rst deleted file mode 100644 index 43d45e4..0000000 --- a/docs/dev/image.rst +++ /dev/null @@ -1,27 +0,0 @@ -##### -Image -##### - -An ``Image`` object (``pymaging.image.Image``) has following required attributes: - -* ``width``: Width of the image in pixels -* ``height``: Height of the image in pixels -* ``pixels``: A list of arrays (``array.array``). Each array in the list is a - **line** in the image. Each array is the actual image data for - that line in the image. -* ``mode``: The color mode (RGB/RGBA). -* ``palette``: The palette object (a list of colors represented as lists) or - ``None`` if the image has no palette. -* ``pixelsize``: The size of a pixel in the array. - - -About pixelsize -=============== - -If ``pixelsize`` is ``1`` and the ``palette`` is set, the actual values stored -in ``pixels`` are actually indices into the ``palette``. So to get the actual -data (color) can be fetched using ``palette[pixelvalue]``. Otherwise you can -get the data (color) using ``pixels[y][x * pixelsize:(x * pixelsize) + pixelsize]``. - -There is also the helper method ``Image.get_color`` to get a color at a x/y -position, this should only be used for very high level operation. diff --git a/docs/dev/index.rst b/docs/dev/index.rst index c696196..758bf2e 100644 --- a/docs/dev/index.rst +++ b/docs/dev/index.rst @@ -15,8 +15,7 @@ Contents :maxdepth: 2 /dev/setup - /dev/overview - /dev/image + /dev/api /dev/incubator /dev/formats /dev/contribute diff --git a/docs/dev/setup.rst b/docs/dev/setup.rst index 950c084..fb248ef 100644 --- a/docs/dev/setup.rst +++ b/docs/dev/setup.rst @@ -4,5 +4,5 @@ Setup for development Clone the git repository using ``git clone https://github.com/ojii/pymaging.git``. -To run the code, you need to be in a virtualenv that has ``distribute`` -installed. For the tests you can use ``nose`` but it is not required. +To run the tests, simply execute ``setup.py test`` with a Python version of your choice, or run ``./runtests.sh`` to run +it against all installed (and supported) Python versions. diff --git a/pymaging/__init__.py b/pymaging/__init__.py index 5c6d163..f6386a5 100644 --- a/pymaging/__init__.py +++ b/pymaging/__init__.py @@ -25,4 +25,4 @@ from pymaging.image import Image -__version__ = '0.0.0' \ No newline at end of file +__version__ = '0.1' diff --git a/pymaging/exceptions.py b/pymaging/exceptions.py index 318a8ee..031ee64 100644 --- a/pymaging/exceptions.py +++ b/pymaging/exceptions.py @@ -27,7 +27,5 @@ class PymagingException(Exception): pass -class ImageModeError(PymagingException): pass -class ImageSizeMismatch(PymagingException): pass class FormatNotSupported(PymagingException): pass class InvalidColor(PymagingException): pass diff --git a/pymaging/formats.py b/pymaging/formats.py index 5f67254..56ef1a2 100644 --- a/pymaging/formats.py +++ b/pymaging/formats.py @@ -60,10 +60,6 @@ def register(self, format): for extension in format.extensions: self.names[extension] = format - def get_formats(self): - self._populate() - return self.registry - def get_format_objects(self): self._populate() return self.formats @@ -73,7 +69,6 @@ def get_format(self, format): return self.names.get(format, None) registry = FormatRegistry() -get_formats = registry.get_formats get_format_objects = registry.get_format_objects get_format = registry.get_format register = registry.register diff --git a/pymaging/helpers.py b/pymaging/helpers.py index ff501a9..5ab5ae3 100644 --- a/pymaging/helpers.py +++ b/pymaging/helpers.py @@ -23,26 +23,7 @@ # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import array from math import ceil -from collections import deque - -class Fliprow(object): - def __init__(self, rowlength, pixelsize): - self.indices = deque() - indicesappend = self.indices.append - tmp = deque() - append = tmp.append - pop = tmp.pop - for i in range(rowlength - 1, -1, -1): - append(i) - if not i % pixelsize: - while tmp: - indicesappend(pop()) - - def flip(self, row): - return array.array('B', (row[i] for i in self.indices)) def get_transformed_dimensions(transform, box): diff --git a/pymaging/image.py b/pymaging/image.py index d036b42..192cf96 100644 --- a/pymaging/image.py +++ b/pymaging/image.py @@ -27,9 +27,8 @@ from pymaging.affine import AffineTransform from pymaging.exceptions import FormatNotSupported, InvalidColor from pymaging.formats import get_format, get_format_objects -from pymaging.helpers import Fliprow, get_transformed_dimensions +from pymaging.helpers import get_transformed_dimensions from pymaging.resample import nearest -import array import os diff --git a/pymaging/pixelarray.py b/pymaging/pixelarray.py index 00f7308..fbffc5c 100644 --- a/pymaging/pixelarray.py +++ b/pymaging/pixelarray.py @@ -41,12 +41,6 @@ def get(self, x, y): start = self._translate(x, y) return [self.data[start+i] for i in range(self.pixelsize)] - def get_multiple(self, start_x, end_x, start_y, end_y): - """ - Returns a list of pixels (a list of lists of length `self.pixelsize`) in the rectangle start_x/start_y-end_x/end_y - """ - return [[self.get(x, y) for x in range(start_x, end_x)] for y in range(start_y, end_y)] - def set(self, x, y, pixel): """ Sets the pixel (a tuple of length `self.pixelsize`) to the pixel array at x/y diff --git a/pymaging/resample.py b/pymaging/resample.py index e5f666b..f462287 100644 --- a/pymaging/resample.py +++ b/pymaging/resample.py @@ -25,7 +25,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from pymaging.pixelarray import get_pixel_array -__all__ = ('nearest', 'bilinear') +__all__ = ('nearest', 'bilinear', 'Resampler') from pymaging.affine import AffineTransform from pymaging.helpers import get_transformed_dimensions @@ -39,7 +39,6 @@ def __init__(self): raise NotImplementedError( "%r is abstract, instantiate a subclass instead" % Resampler ) - return super(Resampler, self).__init__() def affine(self, source, transform, resize_canvas=True): if resize_canvas: diff --git a/pymaging/shapes.py b/pymaging/shapes.py index 559ff49..946badc 100644 --- a/pymaging/shapes.py +++ b/pymaging/shapes.py @@ -28,7 +28,12 @@ import math -class Pixel(object): +class BaseShape(object): + def iter_pixels(self, color): + raise StopIteration() + + +class Pixel(BaseShape): def __init__(self, x, y): self.x = x self.y = y diff --git a/pymaging/test_utils.py b/pymaging/test_utils.py new file mode 100644 index 0000000..a3c52d3 --- /dev/null +++ b/pymaging/test_utils.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +import unittest +import array +from pymaging.colors import ColorType +from pymaging.image import Image +from pymaging.pixelarray import get_pixel_array + + +def image_factory(colors, alpha=True): + height = len(colors) + width = len(colors[0]) if height else 0 + pixel_size = 4 if alpha else 3 + pixel_array = get_pixel_array(array.array('B', [0] * width * height * pixel_size), width, height, pixel_size) + for y in range(height): + for x in range(width): + pixel_array.set(x, y, colors[y][x].to_pixel(pixel_size)) + return Image(pixel_array, ColorType(pixel_size, alpha)) + + +class PymagingBaseTestCase(unittest.TestCase): + def assertImage(self, img, colors, alpha=True): + check = image_factory(colors, alpha) + self.assertEqual(img.pixels, check.pixels) diff --git a/pymaging/tests/test_basic.py b/pymaging/tests/test_basic.py index 62f53a7..6d26200 100644 --- a/pymaging/tests/test_basic.py +++ b/pymaging/tests/test_basic.py @@ -23,14 +23,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from __future__ import absolute_import -from pymaging.colors import Color, ColorType +from pymaging.colors import Color from pymaging.exceptions import FormatNotSupported from pymaging.image import Image -from pymaging.pixelarray import get_pixel_array from pymaging.shapes import Line, Pixel +from pymaging.test_utils import PymagingBaseTestCase, image_factory from pymaging.webcolors import Red, Green, Blue, Black, White, Lime -import array -import unittest try: # pragma: no-cover # 2.x from StringIO import StringIO @@ -39,23 +37,6 @@ from io import StringIO -def image_factory(colors, alpha=True): - height = len(colors) - width = len(colors[0]) if height else 0 - pixelsize = 4 if alpha else 3 - pixel_array = get_pixel_array(array.array('B', [0] * width * height * pixelsize), width, height, pixelsize) - for y in range(height): - for x in range(width): - pixel_array.set(x, y, colors[y][x].to_pixel(pixelsize)) - return Image(pixel_array, ColorType(pixelsize, alpha)) - - -class PymagingBaseTestCase(unittest.TestCase): - def assertImage(self, img, colors, alpha=True): - check = image_factory(colors, alpha) - self.assertEqual(img.pixels, check.pixels) - - class BasicTests(PymagingBaseTestCase): def _get_fake_image(self): return image_factory([ diff --git a/runtests.sh b/runtests.sh index c1c84b9..c3d9351 100755 --- a/runtests.sh +++ b/runtests.sh @@ -2,6 +2,7 @@ PYTHON_VERSIONS="2.6 2.7 3.1 3.2 3.3" COMMAND="setup.py test" +STATUS=0 for version in $PYTHON_VERSIONS; do pybin="python$version" @@ -10,6 +11,7 @@ for version in $PYTHON_VERSIONS; do echo "Running tests for Python $version" echo "****************************" $pybin $COMMAND + STATUS=$(($STATUS+$?)) else echo "****************************" echo "Python $version not found, skipping" @@ -23,4 +25,12 @@ if [ `which pypy` ]; then echo "Running tests for PyPy $pypyversion" echo "**************************************************" pypy $COMMAND + STATUS=$(($STATUS+$?)) fi +echo +if [ $STATUS -eq 0 ]; then + echo "All versions OK" +else + echo "One or more versions FAILED" +fi +