From: foamyguy Date: Tue, 29 Apr 2025 12:37:35 +0000 (-0500) Subject: adding docstrings and copyright. X-Git-Tag: 2.2.0^2~3 X-Git-Url: https://git.ayoreis.com/hackapet/Adafruit_Blinka_Displayio.git/commitdiff_plain/f28da0ef103057447e330a77dc7deb4ea1cab5eb adding docstrings and copyright. --- diff --git a/bitmaptools/__init__.py b/bitmaptools/__init__.py index f27bad4..4e7a689 100644 --- a/bitmaptools/__init__.py +++ b/bitmaptools/__init__.py @@ -1,3 +1,10 @@ +# SPDX-FileCopyrightText: 2025 Tim Cocks +# +# SPDX-License-Identifier: MIT +""" +Collection of bitmap manipulation tools +""" + import math import struct from typing import Optional, Tuple, BinaryIO @@ -7,12 +14,32 @@ from displayio import Bitmap, Colorspace # pylint: disable=invalid-name, too-many-arguments, too-many-locals, too-many-branches, too-many-statements def fill_region(dest_bitmap: Bitmap, x1: int, y1: int, x2: int, y2: int, value: int): + """Draws the color value into the destination bitmap within the + rectangular region bounded by (x1,y1) and (x2,y2), exclusive. + + :param bitmap dest_bitmap: Destination bitmap that will be written into + :param int x1: x-pixel position of the first corner of the rectangular fill region + :param int y1: y-pixel position of the first corner of the rectangular fill region + :param int x2: x-pixel position of the second corner of the rectangular fill region (exclusive) + :param int y2: y-pixel position of the second corner of the rectangular fill region (exclusive) + :param int value: Bitmap palette index that will be written into the rectangular + fill region in the destination bitmap""" + for y in range(y1, y2): for x in range(x1, x2): dest_bitmap[x, y] = value def draw_line(dest_bitmap: Bitmap, x1: int, y1: int, x2: int, y2: int, value: int): + """Draws a line into a bitmap specified two endpoints (x1,y1) and (x2,y2). + + :param bitmap dest_bitmap: Destination bitmap that will be written into + :param int x1: x-pixel position of the line's first endpoint + :param int y1: y-pixel position of the line's first endpoint + :param int x2: x-pixel position of the line's second endpoint + :param int y2: y-pixel position of the line's second endpoint + :param int value: Bitmap palette index that will be written into the + line in the destination bitmap""" dx = abs(x2 - x1) sx = 1 if x1 < x2 else -1 dy = -abs(y2 - y1) @@ -33,6 +60,15 @@ def draw_line(dest_bitmap: Bitmap, x1: int, y1: int, x2: int, y2: int, value: in def draw_circle(dest_bitmap: Bitmap, x: int, y: int, radius: int, value: int): + """Draws a circle into a bitmap specified using a center (x0,y0) and radius r. + + :param bitmap dest_bitmap: Destination bitmap that will be written into + :param int x: x-pixel position of the circle's center + :param int y: y-pixel position of the circle's center + :param int radius: circle's radius + :param int value: Bitmap palette index that will be written into the + circle in the destination bitmap""" + x = max(0, min(x, dest_bitmap.width - 1)) y = max(0, min(y, dest_bitmap.height - 1)) @@ -66,6 +102,15 @@ def draw_polygon( value: int, close: bool = True, ): + """Draw a polygon connecting points on provided bitmap with provided value + + :param bitmap dest_bitmap: Destination bitmap that will be written into + :param ReadableBuffer xs: x-pixel position of the polygon's vertices + :param ReadableBuffer ys: y-pixel position of the polygon's vertices + :param int value: Bitmap palette index that will be written into the + line in the destination bitmap + :param bool close: (Optional) Whether to connect first and last point. (True) + """ if len(xs) != len(ys): raise ValueError("Length of xs and ys must be equal.") @@ -107,7 +152,33 @@ def blit( skip_source_index: int | None = None, skip_dest_index: int | None = None, ): - """Inserts the source_bitmap region defined by rectangular boundaries""" + """Inserts the source_bitmap region defined by rectangular boundaries + (x1,y1) and (x2,y2) into the bitmap at the specified (x,y) location. + + :param bitmap dest_bitmap: Destination bitmap that the area will + be copied into. + :param bitmap source_bitmap: Source bitmap that contains the graphical + region to be copied + :param int x: Horizontal pixel location in bitmap where source_bitmap + upper-left corner will be placed + :param int y: Vertical pixel location in bitmap where source_bitmap upper-left + corner will be placed + :param int x1: Minimum x-value for rectangular bounding box to be + copied from the source bitmap + :param int y1: Minimum y-value for rectangular bounding box to be + copied from the source bitmap + :param int x2: Maximum x-value (exclusive) for rectangular + bounding box to be copied from the source bitmap. + If unspecified or `None`, the source bitmap width is used. + :param int y2: Maximum y-value (exclusive) for rectangular + bounding box to be copied from the source bitmap. + If unspecified or `None`, the source bitmap height is used. + :param int skip_source_index: bitmap palette index in the source that + will not be copied, set to None to copy all pixels + :param int skip_dest_index: bitmap palette index in the + destination bitmap that will not get overwritten by the + pixels from the source""" + # pylint: disable=invalid-name if x2 is None: x2 = source_bitmap.width @@ -167,6 +238,37 @@ def rotozoom( scale: Optional[float] = None, skip_index: Optional[int] = None, ): + """Inserts the source bitmap region into the destination bitmap with rotation + (angle), scale and clipping (both on source and destination bitmaps). + + :param bitmap dest_bitmap: Destination bitmap that will be copied into + :param bitmap source_bitmap: Source bitmap that contains the graphical region to be copied + :param int ox: Horizontal pixel location in destination bitmap where source bitmap + point (px,py) is placed. Defaults to None which causes it to use the horizontal + midway point of the destination bitmap. + :param int oy: Vertical pixel location in destination bitmap where source bitmap + point (px,py) is placed. Defaults to None which causes it to use the vertical + midway point of the destination bitmap. + :param Tuple[int,int] dest_clip0: First corner of rectangular destination clipping + region that constrains region of writing into destination bitmap + :param Tuple[int,int] dest_clip1: Second corner of rectangular destination clipping + region that constrains region of writing into destination bitmap + :param int px: Horizontal pixel location in source bitmap that is placed into the + destination bitmap at (ox,oy). Defaults to None which causes it to use the + horizontal midway point in the source bitmap. + :param int py: Vertical pixel location in source bitmap that is placed into the + destination bitmap at (ox,oy). Defaults to None which causes it to use the + vertical midway point in the source bitmap. + :param Tuple[int,int] source_clip0: First corner of rectangular source clipping + region that constrains region of reading from the source bitmap + :param Tuple[int,int] source_clip1: Second corner of rectangular source clipping + region that constrains region of reading from the source bitmap + :param float angle: Angle of rotation, in radians (positive is clockwise direction). + Defaults to None which gets treated as 0.0 radians or no rotation. + :param float scale: Scaling factor. Defaults to None which gets treated as 1.0 or same + as original source size. + :param int skip_index: Bitmap palette index in the source that will not be copied, + set to None to copy all pixels""" if ox is None: ox = dest_bitmap.width // 2 if oy is None: @@ -277,6 +379,35 @@ def arrayblit( y2: Optional[int] = None, skip_index: Optional[int] = None, ): + """Inserts pixels from ``data`` into the rectangle + of width×height pixels with the upper left corner at ``(x,y)`` + + The values from ``data`` are taken modulo the number of color values + available in the destination bitmap. + + If x1 or y1 are not specified, they are taken as 0. If x2 or y2 + are not specified, or are given as None, they are taken as the width + and height of the image. + + The coordinates affected by the blit are + ``x1 <= x < x2`` and ``y1 <= y < y2``. + + ``data`` must contain at least as many elements as required. If it + contains excess elements, they are ignored. + + The blit takes place by rows, so the first elements of ``data`` go + to the first row, the next elements to the next row, and so on. + + :param displayio.Bitmap bitmap: A writable bitmap + :param ReadableBuffer data: Buffer containing the source pixel values + :param int x1: The left corner of the area to blit into (inclusive) + :param int y1: The top corner of the area to blit into (inclusive) + :param int x2: The right of the area to blit into (exclusive) + :param int y2: The bottom corner of the area to blit into (exclusive) + :param int skip_index: Bitmap palette index in the source + that will not be copied, set to None to copy all pixels + + """ if x2 is None: x2 = bitmap.width if y2 is None: @@ -300,6 +431,34 @@ def readinto( swap_bytes: bool = False, reverse_rows: bool = False, ): + """Reads from a binary file into a bitmap. + + The file must be positioned so that it consists of ``bitmap.height`` + rows of pixel data, where each row is the smallest multiple + of ``element_size`` bytes that can hold ``bitmap.width`` pixels. + + The bytes in an element can be optionally swapped, and the pixels + in an element can be reversed. Also, therow loading direction can + be reversed, which may be requires for loading certain bitmap files. + + This function doesn't parse image headers, but is useful to + speed up loading of uncompressed image formats such as PCF glyph data. + + :param displayio.Bitmap bitmap: A writable bitmap + :param typing.BinaryIO file: A file opened in binary mode + :param int bits_per_pixel: Number of bits per pixel. + Values 1, 2, 4, 8, 16, 24, and 32 are supported; + :param int element_size: Number of bytes per element. + Values of 1, 2, and 4 are supported, except that 24 + ``bits_per_pixel`` requires 1 byte per element. + :param bool reverse_pixels_in_element: If set, the first pixel in a + word is taken from the Most Significant Bits; otherwise, + it is taken from the Least Significant Bits. + :param bool swap_bytes_in_element: If the ``element_size`` is not 1, + then reverse the byte order of each element read. + :param bool reverse_rows: Reverse the direction of the row loading + (required for some bitmap images). + """ width = bitmap.width height = bitmap.height bits_per_value = bitmap._bits_per_value @@ -370,6 +529,10 @@ def readinto( class BlendMode: + """ + Options for modes to use by alphablend() function. + """ + Normal = "bitmaptools.BlendMode.Normal" Screen = "bitmaptools.BlendMode.Screen" @@ -385,11 +548,28 @@ def alphablend( skip_source1_index: Optional[int] = None, skip_source2_index: Optional[int] = None, ): - """ - colorspace should be one of: 'L8', 'RGB565', 'RGB565_SWAPPED', 'BGR565_SWAPPED'. - - blendmode must be one of the BlendMode constants - """ + """Alpha blend the two source bitmaps into the destination. + + It is permitted for the destination bitmap to be one of the two + source bitmaps. + + :param bitmap dest_bitmap: Destination bitmap that will be written into + :param bitmap source_bitmap_1: The first source bitmap + :param bitmap source_bitmap_2: The second source bitmap + :param float factor1: The proportion of bitmap 1 to mix in + :param float factor2: The proportion of bitmap 2 to mix in. + If specified as `None`, ``1-factor1`` is used. Usually the proportions should sum to 1. + :param displayio.Colorspace colorspace: The colorspace of the bitmaps. + They must all have the same colorspace. Only the following colorspaces are permitted: + ``L8``, ``RGB565``, ``RGB565_SWAPPED``, ``BGR565`` and ``BGR565_SWAPPED``. + :param bitmaptools.BlendMode blendmode: The blend mode to use. Default is Normal. + :param int skip_source1_index: Bitmap palette or luminance index in + source_bitmap_1 that will not be blended, set to None to blend all pixels + :param int skip_source2_index: Bitmap palette or luminance index in + source_bitmap_2 that will not be blended, set to None to blend all pixels + + For the L8 colorspace, the bitmaps must have a bits-per-value of 8. + For the RGB colorspaces, they must have a bits-per-value of 16.""" def clamp(val, minval, maxval): return max(minval, min(maxval, val)) @@ -492,6 +672,10 @@ def alphablend( class DitherAlgorithm: + """ + Options for algorithm to use by dither() function. + """ + Atkinson = "bitmaptools.DitherAlgorithm.Atkinson" FloydStenberg = "bitmaptools.DitherAlgorithm.FloydStenberg" @@ -522,6 +706,16 @@ class DitherAlgorithm: def dither(dest_bitmap, source_bitmap, colorspace, algorithm=DitherAlgorithm.Atkinson): + """Convert the input image into a 2-level output image using the given dither algorithm. + + :param bitmap dest_bitmap: Destination bitmap. It must have + a value_count of 2 or 65536. The stored values are 0 and the maximum pixel value. + :param bitmap source_bitmap: Source bitmap that contains the + graphical region to be dithered. It must have a value_count of 65536. + :param colorspace: The colorspace of the image. The supported colorspaces + are ``RGB565``, ``BGR565``, ``RGB565_SWAPPED``, and ``BGR565_SWAPPED`` + :param algorithm: The dither algorithm to use, one of the `DitherAlgorithm` values. + """ SWAP_BYTES = 1 << 0 SWAP_RB = 1 << 1 height, width = dest_bitmap.width, dest_bitmap.height @@ -667,6 +861,17 @@ def boundary_fill( fill_color_value: int, replaced_color_value: Optional[int] = None, ): + """Draws the color value into the destination bitmap enclosed + area of pixels of the background_value color. Like "Paint Bucket" + fill tool. + + :param bitmap dest_bitmap: Destination bitmap that will be written into + :param int x: x-pixel position of the first pixel to check and fill if needed + :param int y: y-pixel position of the first pixel to check and fill if needed + :param int fill_color_value: Bitmap palette index that will be written into the + enclosed area in the destination bitmap + :param int replaced_color_value: Bitmap palette index that will filled with the + value color in the enclosed area in the destination bitmap""" if fill_color_value == replaced_color_value: return if replaced_color_value == -1: