]> Repositories - hackapet/Adafruit_Blinka_Displayio.git/blobdiff - bitmaptools/__init__.py
alphablend initial code
[hackapet/Adafruit_Blinka_Displayio.git] / bitmaptools / __init__.py
index e3490f653531a700887e2d7f593a1e40cec364fd..0c9465bfcdb53a27a02d7ef9335055eae9558dd3 100644 (file)
@@ -1,7 +1,8 @@
 import math
-from typing import Optional, Tuple
+import struct
+from typing import Optional, Tuple, BinaryIO
 
-from displayio import Bitmap
+from displayio import Bitmap, Colorspace
 import circuitpython_typing
 
 
@@ -224,7 +225,6 @@ def arrayblit(
         x1: int = 0, y1: int = 0,
         x2: int | None = None, y2: int | None = None,
         skip_index: int | None = None):
-
     if x2 is None:
         x2 = bitmap.width
     if y2 is None:
@@ -237,3 +237,185 @@ def arrayblit(
             value = int(data[i] % _value_count)
             if skip_index is None or value != skip_index:
                 bitmap[x, y] = value
+
+
+def readinto(bitmap: Bitmap,
+             file: BinaryIO,
+             bits_per_pixel: int,
+             element_size: int = 1,
+             reverse_pixels_in_element: bool = False,
+             swap_bytes: bool = False,
+             reverse_rows: bool = False):
+
+    width = bitmap.width
+    height = bitmap.height
+    bits_per_value = bitmap._bits_per_value
+    mask = (1 << bits_per_value) - 1
+
+    elements_per_row = (width * bits_per_pixel + element_size * 8 - 1) // (element_size * 8)
+    rowsize = element_size * elements_per_row
+
+    for y in range(height):
+        row_bytes = file.read(rowsize)
+        if len(row_bytes) != rowsize:
+            raise EOFError()
+
+        # Convert the raw bytes into the appropriate type array for processing
+        rowdata = bytearray(row_bytes)
+
+        if swap_bytes:
+            if element_size == 2:
+                rowdata = bytearray(
+                    b''.join(
+                        struct.pack('<H', struct.unpack('>H', rowdata[i:i + 2])[0])
+                        for i in range(0, len(rowdata), 2)
+                    )
+                )
+            elif element_size == 4:
+                rowdata = bytearray(
+                    b''.join(
+                        struct.pack('<I', struct.unpack('>I', rowdata[i:i + 4])[0])
+                        for i in range(0, len(rowdata), 4)
+                    )
+                )
+
+        y_draw = height - 1 - y if reverse_rows else y
+
+        for x in range(width):
+            value = 0
+            if bits_per_pixel == 1:
+                byte_offset = x // 8
+                bit_offset = 7 - (x % 8) if reverse_pixels_in_element else x % 8
+                value = (rowdata[byte_offset] >> bit_offset) & 0x1
+            elif bits_per_pixel == 2:
+                byte_offset = x // 4
+                bit_index = 3 - (x % 4) if reverse_pixels_in_element else x % 4
+                bit_offset = 2 * bit_index
+                value = (rowdata[byte_offset] >> bit_offset) & 0x3
+            elif bits_per_pixel == 4:
+                byte_offset = x // 2
+                bit_index = 1 - (x % 2) if reverse_pixels_in_element else x % 2
+                bit_offset = 4 * bit_index
+                value = (rowdata[byte_offset] >> bit_offset) & 0xF
+            elif bits_per_pixel == 8:
+                value = rowdata[x]
+            elif bits_per_pixel == 16:
+                value = struct.unpack_from('<H', rowdata, x * 2)[0]
+            elif bits_per_pixel == 24:
+                offset = x * 3
+                value = (rowdata[offset] << 16) | (rowdata[offset + 1] << 8) | rowdata[offset + 2]
+            elif bits_per_pixel == 32:
+                value = struct.unpack_from('<I', rowdata, x * 4)[0]
+
+            bitmap[x, y_draw] = value & mask
+
+def alphablend(dest:Bitmap, source1:Bitmap, source2:Bitmap, colorspace:Colorspace, factor1:float, factor2:float,
+               blendmode, skip_source1_index:int=None,
+               skip_source2_index:int=None):
+
+    """
+    colorspace should be one of: 'L8', 'RGB565', 'RGB565_SWAPPED', 'BGR565_SWAPPED'.
+
+blendmode can be 'normal' (or any default) or 'screen'.
+
+This assumes that all bitmaps (dest, source1, source2) support 2D access like bitmap[x, y].
+
+dest.width and dest.height are used; make sure the bitmap objects have these attributes or replace them with your own logic.
+    """
+    def clamp(val, minval, maxval):
+        return max(minval, min(maxval, val))
+
+    ifactor1 = int(factor1 * 256)
+    ifactor2 = int(factor2 * 256)
+
+    width, height = dest.width, dest.height
+
+    if colorspace == 'L8':
+        for y in range(height):
+            for x in range(width):
+                sp1 = source1[x, y]
+                sp2 = source2[x, y]
+                blend_source1 = skip_source1_index is None or sp1 != skip_source1_index
+                blend_source2 = skip_source2_index is None or sp2 != skip_source2_index
+
+                if blend_source1 and blend_source2:
+                    sda = sp1 * ifactor1
+                    sca = sp2 * ifactor2
+
+                    if blendmode == 'screen':
+                        blend = sca + sda - (sca * sda // 65536)
+                    else:
+                        blend = sca + sda * (256 - ifactor2) // 256
+
+                    denom = ifactor1 + ifactor2 - ifactor1 * ifactor2 // 256
+                    pixel = blend // denom
+                elif blend_source1:
+                    pixel = sp1 * ifactor1 // 256
+                elif blend_source2:
+                    pixel = sp2 * ifactor2 // 256
+                else:
+                    pixel = dest[x, y]
+
+                dest[x, y] = clamp(pixel, 0, 255)
+
+    else:
+        swap = colorspace in ('RGB565_SWAPPED', 'BGR565_SWAPPED')
+        r_mask = 0xf800
+        g_mask = 0x07e0
+        b_mask = 0x001f
+
+        for y in range(height):
+            for x in range(width):
+                sp1 = source1[x, y]
+                sp2 = source2[x, y]
+
+                if swap:
+                    sp1 = ((sp1 & 0xFF) << 8) | ((sp1 >> 8) & 0xFF)
+                    sp2 = ((sp2 & 0xFF) << 8) | ((sp2 >> 8) & 0xFF)
+
+                blend_source1 = skip_source1_index_none or sp1 != skip_source1_index
+                blend_source2 = skip_source2_index_none or sp2 != skip_source2_index
+
+                if blend_source1 and blend_source2:
+                    ifactor_blend = ifactor1 + ifactor2 - ifactor1 * ifactor2 // 256
+
+                    red_dca = ((sp1 & r_mask) >> 8) * ifactor1
+                    grn_dca = ((sp1 & g_mask) >> 3) * ifactor1
+                    blu_dca = ((sp1 & b_mask) << 3) * ifactor1
+
+                    red_sca = ((sp2 & r_mask) >> 8) * ifactor2
+                    grn_sca = ((sp2 & g_mask) >> 3) * ifactor2
+                    blu_sca = ((sp2 & b_mask) << 3) * ifactor2
+
+                    if blendmode == 'screen':
+                        red_blend = red_sca + red_dca - (red_sca * red_dca // 65536)
+                        grn_blend = grn_sca + grn_dca - (grn_sca * grn_dca // 65536)
+                        blu_blend = blu_sca + blu_dca - (blu_sca * blu_dca // 65536)
+                    else:
+                        red_blend = red_sca + red_dca * (256 - ifactor2) // 256
+                        grn_blend = grn_sca + grn_dca * (256 - ifactor2) // 256
+                        blu_blend = blu_sca + blu_dca * (256 - ifactor2) // 256
+
+                    r = ((red_blend // ifactor_blend) << 8) & r_mask
+                    g = ((grn_blend // ifactor_blend) << 3) & g_mask
+                    b = ((blu_blend // ifactor_blend) >> 3) & b_mask
+
+                    pixel = (r & r_mask) | (g & g_mask) | (b & b_mask)
+
+                    if swap:
+                        pixel = ((pixel & 0xFF) << 8) | ((pixel >> 8) & 0xFF)
+
+                elif blend_source1:
+                    r = ((sp1 & r_mask) * ifactor1 // 256) & r_mask
+                    g = ((sp1 & g_mask) * ifactor1 // 256) & g_mask
+                    b = ((sp1 & b_mask) * ifactor1 // 256) & b_mask
+                    pixel = r | g | b
+                elif blend_source2:
+                    r = ((sp2 & r_mask) * ifactor2 // 256) & r_mask
+                    g = ((sp2 & g_mask) * ifactor2 // 256) & g_mask
+                    b = ((sp2 & b_mask) * ifactor2 // 256) & b_mask
+                    pixel = r | g | b
+                else:
+                    pixel = dest[x, y]
+
+                dest[x, y] = pixel