Skip to content

Commit f28da0e

Browse files
committed
adding docstrings and copyright.
1 parent 293a56c commit f28da0e

File tree

1 file changed

+211
-6
lines changed

1 file changed

+211
-6
lines changed

bitmaptools/__init__.py

Lines changed: 211 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# SPDX-FileCopyrightText: 2025 Tim Cocks
2+
#
3+
# SPDX-License-Identifier: MIT
4+
"""
5+
Collection of bitmap manipulation tools
6+
"""
7+
18
import math
29
import struct
310
from typing import Optional, Tuple, BinaryIO
@@ -7,12 +14,32 @@
714

815
# pylint: disable=invalid-name, too-many-arguments, too-many-locals, too-many-branches, too-many-statements
916
def fill_region(dest_bitmap: Bitmap, x1: int, y1: int, x2: int, y2: int, value: int):
17+
"""Draws the color value into the destination bitmap within the
18+
rectangular region bounded by (x1,y1) and (x2,y2), exclusive.
19+
20+
:param bitmap dest_bitmap: Destination bitmap that will be written into
21+
:param int x1: x-pixel position of the first corner of the rectangular fill region
22+
:param int y1: y-pixel position of the first corner of the rectangular fill region
23+
:param int x2: x-pixel position of the second corner of the rectangular fill region (exclusive)
24+
:param int y2: y-pixel position of the second corner of the rectangular fill region (exclusive)
25+
:param int value: Bitmap palette index that will be written into the rectangular
26+
fill region in the destination bitmap"""
27+
1028
for y in range(y1, y2):
1129
for x in range(x1, x2):
1230
dest_bitmap[x, y] = value
1331

1432

1533
def draw_line(dest_bitmap: Bitmap, x1: int, y1: int, x2: int, y2: int, value: int):
34+
"""Draws a line into a bitmap specified two endpoints (x1,y1) and (x2,y2).
35+
36+
:param bitmap dest_bitmap: Destination bitmap that will be written into
37+
:param int x1: x-pixel position of the line's first endpoint
38+
:param int y1: y-pixel position of the line's first endpoint
39+
:param int x2: x-pixel position of the line's second endpoint
40+
:param int y2: y-pixel position of the line's second endpoint
41+
:param int value: Bitmap palette index that will be written into the
42+
line in the destination bitmap"""
1643
dx = abs(x2 - x1)
1744
sx = 1 if x1 < x2 else -1
1845
dy = -abs(y2 - y1)
@@ -33,6 +60,15 @@ def draw_line(dest_bitmap: Bitmap, x1: int, y1: int, x2: int, y2: int, value: in
3360

3461

3562
def draw_circle(dest_bitmap: Bitmap, x: int, y: int, radius: int, value: int):
63+
"""Draws a circle into a bitmap specified using a center (x0,y0) and radius r.
64+
65+
:param bitmap dest_bitmap: Destination bitmap that will be written into
66+
:param int x: x-pixel position of the circle's center
67+
:param int y: y-pixel position of the circle's center
68+
:param int radius: circle's radius
69+
:param int value: Bitmap palette index that will be written into the
70+
circle in the destination bitmap"""
71+
3672
x = max(0, min(x, dest_bitmap.width - 1))
3773
y = max(0, min(y, dest_bitmap.height - 1))
3874

@@ -66,6 +102,15 @@ def draw_polygon(
66102
value: int,
67103
close: bool = True,
68104
):
105+
"""Draw a polygon connecting points on provided bitmap with provided value
106+
107+
:param bitmap dest_bitmap: Destination bitmap that will be written into
108+
:param ReadableBuffer xs: x-pixel position of the polygon's vertices
109+
:param ReadableBuffer ys: y-pixel position of the polygon's vertices
110+
:param int value: Bitmap palette index that will be written into the
111+
line in the destination bitmap
112+
:param bool close: (Optional) Whether to connect first and last point. (True)
113+
"""
69114
if len(xs) != len(ys):
70115
raise ValueError("Length of xs and ys must be equal.")
71116

@@ -107,7 +152,33 @@ def blit(
107152
skip_source_index: int | None = None,
108153
skip_dest_index: int | None = None,
109154
):
110-
"""Inserts the source_bitmap region defined by rectangular boundaries"""
155+
"""Inserts the source_bitmap region defined by rectangular boundaries
156+
(x1,y1) and (x2,y2) into the bitmap at the specified (x,y) location.
157+
158+
:param bitmap dest_bitmap: Destination bitmap that the area will
159+
be copied into.
160+
:param bitmap source_bitmap: Source bitmap that contains the graphical
161+
region to be copied
162+
:param int x: Horizontal pixel location in bitmap where source_bitmap
163+
upper-left corner will be placed
164+
:param int y: Vertical pixel location in bitmap where source_bitmap upper-left
165+
corner will be placed
166+
:param int x1: Minimum x-value for rectangular bounding box to be
167+
copied from the source bitmap
168+
:param int y1: Minimum y-value for rectangular bounding box to be
169+
copied from the source bitmap
170+
:param int x2: Maximum x-value (exclusive) for rectangular
171+
bounding box to be copied from the source bitmap.
172+
If unspecified or `None`, the source bitmap width is used.
173+
:param int y2: Maximum y-value (exclusive) for rectangular
174+
bounding box to be copied from the source bitmap.
175+
If unspecified or `None`, the source bitmap height is used.
176+
:param int skip_source_index: bitmap palette index in the source that
177+
will not be copied, set to None to copy all pixels
178+
:param int skip_dest_index: bitmap palette index in the
179+
destination bitmap that will not get overwritten by the
180+
pixels from the source"""
181+
111182
# pylint: disable=invalid-name
112183
if x2 is None:
113184
x2 = source_bitmap.width
@@ -167,6 +238,37 @@ def rotozoom(
167238
scale: Optional[float] = None,
168239
skip_index: Optional[int] = None,
169240
):
241+
"""Inserts the source bitmap region into the destination bitmap with rotation
242+
(angle), scale and clipping (both on source and destination bitmaps).
243+
244+
:param bitmap dest_bitmap: Destination bitmap that will be copied into
245+
:param bitmap source_bitmap: Source bitmap that contains the graphical region to be copied
246+
:param int ox: Horizontal pixel location in destination bitmap where source bitmap
247+
point (px,py) is placed. Defaults to None which causes it to use the horizontal
248+
midway point of the destination bitmap.
249+
:param int oy: Vertical pixel location in destination bitmap where source bitmap
250+
point (px,py) is placed. Defaults to None which causes it to use the vertical
251+
midway point of the destination bitmap.
252+
:param Tuple[int,int] dest_clip0: First corner of rectangular destination clipping
253+
region that constrains region of writing into destination bitmap
254+
:param Tuple[int,int] dest_clip1: Second corner of rectangular destination clipping
255+
region that constrains region of writing into destination bitmap
256+
:param int px: Horizontal pixel location in source bitmap that is placed into the
257+
destination bitmap at (ox,oy). Defaults to None which causes it to use the
258+
horizontal midway point in the source bitmap.
259+
:param int py: Vertical pixel location in source bitmap that is placed into the
260+
destination bitmap at (ox,oy). Defaults to None which causes it to use the
261+
vertical midway point in the source bitmap.
262+
:param Tuple[int,int] source_clip0: First corner of rectangular source clipping
263+
region that constrains region of reading from the source bitmap
264+
:param Tuple[int,int] source_clip1: Second corner of rectangular source clipping
265+
region that constrains region of reading from the source bitmap
266+
:param float angle: Angle of rotation, in radians (positive is clockwise direction).
267+
Defaults to None which gets treated as 0.0 radians or no rotation.
268+
:param float scale: Scaling factor. Defaults to None which gets treated as 1.0 or same
269+
as original source size.
270+
:param int skip_index: Bitmap palette index in the source that will not be copied,
271+
set to None to copy all pixels"""
170272
if ox is None:
171273
ox = dest_bitmap.width // 2
172274
if oy is None:
@@ -277,6 +379,35 @@ def arrayblit(
277379
y2: Optional[int] = None,
278380
skip_index: Optional[int] = None,
279381
):
382+
"""Inserts pixels from ``data`` into the rectangle
383+
of width×height pixels with the upper left corner at ``(x,y)``
384+
385+
The values from ``data`` are taken modulo the number of color values
386+
available in the destination bitmap.
387+
388+
If x1 or y1 are not specified, they are taken as 0. If x2 or y2
389+
are not specified, or are given as None, they are taken as the width
390+
and height of the image.
391+
392+
The coordinates affected by the blit are
393+
``x1 <= x < x2`` and ``y1 <= y < y2``.
394+
395+
``data`` must contain at least as many elements as required. If it
396+
contains excess elements, they are ignored.
397+
398+
The blit takes place by rows, so the first elements of ``data`` go
399+
to the first row, the next elements to the next row, and so on.
400+
401+
:param displayio.Bitmap bitmap: A writable bitmap
402+
:param ReadableBuffer data: Buffer containing the source pixel values
403+
:param int x1: The left corner of the area to blit into (inclusive)
404+
:param int y1: The top corner of the area to blit into (inclusive)
405+
:param int x2: The right of the area to blit into (exclusive)
406+
:param int y2: The bottom corner of the area to blit into (exclusive)
407+
:param int skip_index: Bitmap palette index in the source
408+
that will not be copied, set to None to copy all pixels
409+
410+
"""
280411
if x2 is None:
281412
x2 = bitmap.width
282413
if y2 is None:
@@ -300,6 +431,34 @@ def readinto(
300431
swap_bytes: bool = False,
301432
reverse_rows: bool = False,
302433
):
434+
"""Reads from a binary file into a bitmap.
435+
436+
The file must be positioned so that it consists of ``bitmap.height``
437+
rows of pixel data, where each row is the smallest multiple
438+
of ``element_size`` bytes that can hold ``bitmap.width`` pixels.
439+
440+
The bytes in an element can be optionally swapped, and the pixels
441+
in an element can be reversed. Also, therow loading direction can
442+
be reversed, which may be requires for loading certain bitmap files.
443+
444+
This function doesn't parse image headers, but is useful to
445+
speed up loading of uncompressed image formats such as PCF glyph data.
446+
447+
:param displayio.Bitmap bitmap: A writable bitmap
448+
:param typing.BinaryIO file: A file opened in binary mode
449+
:param int bits_per_pixel: Number of bits per pixel.
450+
Values 1, 2, 4, 8, 16, 24, and 32 are supported;
451+
:param int element_size: Number of bytes per element.
452+
Values of 1, 2, and 4 are supported, except that 24
453+
``bits_per_pixel`` requires 1 byte per element.
454+
:param bool reverse_pixels_in_element: If set, the first pixel in a
455+
word is taken from the Most Significant Bits; otherwise,
456+
it is taken from the Least Significant Bits.
457+
:param bool swap_bytes_in_element: If the ``element_size`` is not 1,
458+
then reverse the byte order of each element read.
459+
:param bool reverse_rows: Reverse the direction of the row loading
460+
(required for some bitmap images).
461+
"""
303462
width = bitmap.width
304463
height = bitmap.height
305464
bits_per_value = bitmap._bits_per_value
@@ -370,6 +529,10 @@ def readinto(
370529

371530

372531
class BlendMode:
532+
"""
533+
Options for modes to use by alphablend() function.
534+
"""
535+
373536
Normal = "bitmaptools.BlendMode.Normal"
374537
Screen = "bitmaptools.BlendMode.Screen"
375538

@@ -385,11 +548,28 @@ def alphablend(
385548
skip_source1_index: Optional[int] = None,
386549
skip_source2_index: Optional[int] = None,
387550
):
388-
"""
389-
colorspace should be one of: 'L8', 'RGB565', 'RGB565_SWAPPED', 'BGR565_SWAPPED'.
390-
391-
blendmode must be one of the BlendMode constants
392-
"""
551+
"""Alpha blend the two source bitmaps into the destination.
552+
553+
It is permitted for the destination bitmap to be one of the two
554+
source bitmaps.
555+
556+
:param bitmap dest_bitmap: Destination bitmap that will be written into
557+
:param bitmap source_bitmap_1: The first source bitmap
558+
:param bitmap source_bitmap_2: The second source bitmap
559+
:param float factor1: The proportion of bitmap 1 to mix in
560+
:param float factor2: The proportion of bitmap 2 to mix in.
561+
If specified as `None`, ``1-factor1`` is used. Usually the proportions should sum to 1.
562+
:param displayio.Colorspace colorspace: The colorspace of the bitmaps.
563+
They must all have the same colorspace. Only the following colorspaces are permitted:
564+
``L8``, ``RGB565``, ``RGB565_SWAPPED``, ``BGR565`` and ``BGR565_SWAPPED``.
565+
:param bitmaptools.BlendMode blendmode: The blend mode to use. Default is Normal.
566+
:param int skip_source1_index: Bitmap palette or luminance index in
567+
source_bitmap_1 that will not be blended, set to None to blend all pixels
568+
:param int skip_source2_index: Bitmap palette or luminance index in
569+
source_bitmap_2 that will not be blended, set to None to blend all pixels
570+
571+
For the L8 colorspace, the bitmaps must have a bits-per-value of 8.
572+
For the RGB colorspaces, they must have a bits-per-value of 16."""
393573

394574
def clamp(val, minval, maxval):
395575
return max(minval, min(maxval, val))
@@ -492,6 +672,10 @@ def clamp(val, minval, maxval):
492672

493673

494674
class DitherAlgorithm:
675+
"""
676+
Options for algorithm to use by dither() function.
677+
"""
678+
495679
Atkinson = "bitmaptools.DitherAlgorithm.Atkinson"
496680
FloydStenberg = "bitmaptools.DitherAlgorithm.FloydStenberg"
497681

@@ -522,6 +706,16 @@ class DitherAlgorithm:
522706

523707

524708
def dither(dest_bitmap, source_bitmap, colorspace, algorithm=DitherAlgorithm.Atkinson):
709+
"""Convert the input image into a 2-level output image using the given dither algorithm.
710+
711+
:param bitmap dest_bitmap: Destination bitmap. It must have
712+
a value_count of 2 or 65536. The stored values are 0 and the maximum pixel value.
713+
:param bitmap source_bitmap: Source bitmap that contains the
714+
graphical region to be dithered. It must have a value_count of 65536.
715+
:param colorspace: The colorspace of the image. The supported colorspaces
716+
are ``RGB565``, ``BGR565``, ``RGB565_SWAPPED``, and ``BGR565_SWAPPED``
717+
:param algorithm: The dither algorithm to use, one of the `DitherAlgorithm` values.
718+
"""
525719
SWAP_BYTES = 1 << 0
526720
SWAP_RB = 1 << 1
527721
height, width = dest_bitmap.width, dest_bitmap.height
@@ -667,6 +861,17 @@ def boundary_fill(
667861
fill_color_value: int,
668862
replaced_color_value: Optional[int] = None,
669863
):
864+
"""Draws the color value into the destination bitmap enclosed
865+
area of pixels of the background_value color. Like "Paint Bucket"
866+
fill tool.
867+
868+
:param bitmap dest_bitmap: Destination bitmap that will be written into
869+
:param int x: x-pixel position of the first pixel to check and fill if needed
870+
:param int y: y-pixel position of the first pixel to check and fill if needed
871+
:param int fill_color_value: Bitmap palette index that will be written into the
872+
enclosed area in the destination bitmap
873+
:param int replaced_color_value: Bitmap palette index that will filled with the
874+
value color in the enclosed area in the destination bitmap"""
670875
if fill_color_value == replaced_color_value:
671876
return
672877
if replaced_color_value == -1:

0 commit comments

Comments
 (0)