Files
pikasTech-HWLAB/skills/arm2d-skill/python/__img2c_zhRGB565.py
T
Codex Agent 712db9c597 feat: arm2d-skill directory seed for CaseRun agent workspace
Add B1 directory-type seed mechanism to AGENT_WORKSPACE_SEED_FILES:
- Extended agentWorkspaceFilesForRun with collectDirectorySeedFiles helper
- arm2d-skill (SKILL.md + references/ + python/) auto-injected to .agents/skills/arm2d-skill/
- Enables Code Agent to follow ARM-2D constraints and call asset scripts in case

Skill source: https://github.com/notLabyet/HWLabOA
2026-06-07 14:40:48 +08:00

747 lines
28 KiB
Python

"""zhRGB565 Encoder Library
Combines RLE and RLE+Differential encoding implementations
Strictly follows C implementation data types and behavior
"""
import numpy as np
from typing import Tuple, Optional
# Encoding thresholds
RLE_THRESHOLD = 3 # Minimum consecutive pixels for RLE encoding
DIFF_THRESHOLD = 7 # Minimum pixels for differential encoding
def rgb565_get_r_u8(color: np.uint16) -> np.uint8:
"""Extract RGB565 red component (5 bits) - matches C uint8_t"""
return np.uint8((color >> 11) & 0x1F)
def rgb565_get_g_u8(color: np.uint16) -> np.uint8:
"""Extract RGB565 green component (6 bits) - matches C uint8_t"""
return np.uint8((color >> 5) & 0x3F)
def rgb565_get_b_u8(color: np.uint16) -> np.uint8:
"""Extract RGB565 blue component (5 bits) - matches C uint8_t"""
return np.uint8(color & 0x1F)
def rgb332_val(r: np.uint8, g: np.uint8, b: np.uint8) -> np.uint8:
"""Pack RGB components into RGB332 format - matches C uint8_t"""
return np.uint8(((r & 0x07) << 5) | ((g & 0x07) << 2) | (b & 0x03))
def pack_u8_to_u16(high: np.uint8, low: np.uint8) -> np.uint16:
"""Pack two uint8 values into one uint16 - matches C uint16_t"""
return np.uint16((np.uint16(high) << 8) | np.uint16(low))
def can_compress_diff(diff: np.uint16) -> bool:
"""
Check if difference can be compressed into one byte - matches C implementation exactly
Condition: R component <=7, G component <=7, B component <=3
"""
r = rgb565_get_r_u8(diff)
g = rgb565_get_g_u8(diff)
b = rgb565_get_b_u8(diff)
return (r <= np.uint8(7)) and (g <= np.uint8(7)) and (b <= np.uint8(3))
def compress_diff_to_byte(diff: np.uint16) -> np.uint8:
"""Compress difference into one byte (RGB332 format) - matches C uint8_t"""
r = rgb565_get_r_u8(diff)
g = rgb565_get_g_u8(diff)
b = rgb565_get_b_u8(diff)
return rgb332_val(r, g, b)
def find_encode_flag(img: np.ndarray, pixel_count: np.uint64) -> Tuple[np.uint16, np.uint16, np.uint16, bool]:
"""
Find optimal encoding flag - strictly matches C implementation data types
Priority:
1. Unused high byte values (XX00 format)
2. High bytes where all pixels have at least 3 consecutive identical values
3. Least used high bytes
Args:
img: RGB565 image data array
pixel_count: Number of pixels (uint64_t in C)
Returns:
(encode_flag, encode_flag_cs, encode_flag_mode, is_perfect)
encode_flag: Flag value (high byte << 8)
encode_flag_cs: Related info (minimum consecutive count or occurrence count)
encode_flag_mode: Mode (0=unused, 1=RLE perfect, 2=least used)
is_perfect: Whether perfect flag was found
"""
if pixel_count == 0:
return np.uint16(0xFF00), np.uint16(0), np.uint16(0), True
# ========== Step 1: Count basic information ==========
# Match C implementation: uint8_t used_map[32] = {0};
used_map = np.zeros(32, dtype=np.uint8)
# Match C implementation: uint16_t count_map[256] = {0};
# Use uint32_t to prevent overflow with large images
count_map = np.zeros(256, dtype=np.uint32)
for i in range(int(pixel_count)):
pixel = int(img[i])
hi_byte = (pixel >> 8) & 0xFF
used_map[hi_byte // 8] |= np.uint8(1 << (hi_byte % 8))
count_map[hi_byte] += np.uint32(1) # Use uint32_t to prevent overflow
# ========== Step 2: Priority 1 - Unused high bytes ==========
for hi_byte in range(256):
if (int(used_map[hi_byte // 8]) & (1 << (hi_byte % 8))) == 0:
return np.uint16(hi_byte << 8), np.uint16(0), np.uint16(0), True
# ========== Step 3: Priority 2 - Perfect RLE high bytes ==========
for hi_byte in range(256):
if count_map[hi_byte] == 0:
continue
is_perfect = True
min_continuous = np.uint16(0xFFFF)
i = np.uint64(0)
while i < pixel_count:
# Skip pixels that don't have this high byte
while i < pixel_count and ((int(img[int(i)]) >> 8) & 0xFF) != hi_byte:
i += np.uint64(1)
if i >= pixel_count:
break
# Check continuous segments
current_value = np.uint16(img[int(i)])
continuous_count = np.uint16(1)
j = i + np.uint64(1)
while j < pixel_count and np.uint16(img[int(j)]) == current_value:
continuous_count += np.uint16(1)
j += np.uint64(1)
# Check if condition is met (at least 3 consecutive identical)
if continuous_count < RLE_THRESHOLD:
is_perfect = False
break
if continuous_count < min_continuous:
min_continuous = continuous_count
i = j
if is_perfect and min_continuous != np.uint16(0xFFFF):
# Find a specific pixel value as flag
concrete_value = np.uint16(hi_byte << 8)
for k in range(int(pixel_count)):
if ((int(img[k]) >> 8) & 0xFF) == hi_byte:
concrete_value = np.uint16(img[k])
break
return np.uint16(concrete_value & 0xFF00), min_continuous, np.uint16(1), True
# ========== Step 4: Priority 3 - Least used high bytes ==========
min_hi_byte = np.uint16(0)
min_count = np.uint16(0xFFFF)
for hi_byte in range(256):
if count_map[hi_byte] > 0:
if count_map[hi_byte] < min_count:
min_count = count_map[hi_byte]
min_hi_byte = np.uint16(hi_byte)
elif count_map[hi_byte] == min_count:
if hi_byte < min_hi_byte:
min_hi_byte = np.uint16(hi_byte)
if min_count == np.uint16(0xFFFF):
return np.uint16(0xFF00), np.uint16(0), np.uint16(2), False
# Important: In least used mode, return XX00 format
return np.uint16(min_hi_byte << 8), min_count, np.uint16(2), False
def check_rle_length(pixels: np.ndarray, start: np.uint32, end: np.uint32) -> np.uint32:
"""
Check RLE length starting from position - strictly matches C implementation data types
Args:
pixels: Pixel array
start: Start position (uint32_t in C)
end: End position (exclusive, uint32_t in C)
Returns:
Number of consecutive identical pixels (uint32_t)
"""
if start >= end:
return np.uint32(0)
first_color = np.uint16(pixels[int(start)])
length = np.uint32(1)
for i in range(int(start) + 1, int(end)):
if np.uint16(pixels[i]) == first_color:
length += np.uint32(1)
if length == np.uint32(0xFFFF):
break
else:
break
return length
def calculate_diff_length(data: np.ndarray, start: np.uint32, end: np.uint32, output: np.ndarray) -> np.uint16:
"""
Calculate the number of pixels that meet differential encoding criteria - strictly matches C implementation
Args:
data: Pixel data array
start: Start position (uint32_t in C)
end: End position (exclusive, uint32_t in C)
output: Output difference data buffer
Returns:
Compressible difference pixel count (uint16_t in C)
"""
if end - start < DIFF_THRESHOLD:
return np.uint16(0)
idx = np.uint16(0) # CRITICAL: Must be uint16_t like C
idx_tmp = np.uint16(0)
tmp = np.zeros(2, dtype=np.uint8)
# CRITICAL: C uses uint16_t for loop variable, not uint32_t
i = np.uint16(start)
diff_count = np.uint16(1)
rle_cnt = np.uint16(1)
while i < end - np.uint16(1):
current_pixel = np.uint16(data[int(i)])
next_pixel = np.uint16(data[int(i) + 1])
diff = np.uint16(current_pixel ^ next_pixel)
# Meet differential encoding conditions
if can_compress_diff(diff):
diff_count += np.uint16(1)
tmp[int(idx_tmp)] = compress_diff_to_byte(diff)
idx_tmp += np.uint16(1)
if idx_tmp == np.uint16(2):
if int(idx) >= len(output):
# Prevent buffer overflow - match C behavior
break
output[int(idx)] = pack_u8_to_u16(tmp[0], tmp[1])
idx += np.uint16(1)
idx_tmp = np.uint16(0)
if idx == np.uint16(31): # Actual encoded source data 31*2 + 1
break
if diff == np.uint16(0):
# diff=0 means 2 identical pixels
rle_cnt += np.uint16(1)
# Handle uint16_t overflow - if rle_cnt reaches max, treat as exceeding threshold
if rle_cnt == np.uint16(0): # Overflow occurred
diff_count -= np.uint16(RLE_THRESHOLD)
break
if rle_cnt > np.uint16(RLE_THRESHOLD): # CRITICAL: Revert back to >
# Consecutive pixels exceed 3+1, exit for RLE processing
diff_count -= np.uint16(RLE_THRESHOLD)
break
else:
rle_cnt = np.uint16(0)
# CRITICAL: C uses i++ which keeps it as uint16_t
i += np.uint16(1)
else:
break
# CRITICAL: C doesn't have this logic - only adjust for even count
# Handle remaining differences - CRITICAL: Try removing this adjustment
# if idx_tmp == np.uint16(1):
# # Odd number of differences, reduce by one
# diff_count -= np.uint16(1)
# Number of pixels meeting differential encoding must be odd and not zero
if diff_count % np.uint16(2) == np.uint16(0) and diff_count != np.uint16(0):
diff_count -= np.uint16(1)
if diff_count >= np.uint16(DIFF_THRESHOLD):
return diff_count
else:
return np.uint16(0)
def encode_rgb565_rle_only(input_data: np.ndarray, width: np.uint16, height: np.uint16) -> Tuple[Optional[np.ndarray], np.uint32, float]:
"""
Pure RLE encoding function - strictly matches C implementation data types
Args:
input_data: Input RGB565 data, length is width*height
width: Image width (uint16_t in C)
height: Image height (uint16_t in C)
Returns:
(output_data, output_size, compression_ratio)
output_data: Encoded data array
output_size: Encoded data size (uint32_t in C)
compression_ratio: Compression ratio (percentage, smaller is better)
"""
pixel_count = np.uint64(width) * np.uint64(height)
if width == 0 or height == 0 or pixel_count == 0:
return None, np.uint32(0), 0.0
# Find encoding flag - pass pixel_count as uint64_t
encode_flag, encode_flag_cs, encode_flag_mode, flag_ok = find_encode_flag(input_data, pixel_count)
# Estimate maximum output size - match C calculation exactly (uint64_t)
max_output_size = np.uint64(10) + np.uint64(height) + (pixel_count * np.uint64(2))
output = np.zeros(int(max_output_size), dtype=np.uint32) # Use 32-bit to avoid overflow during construction
# Allocate row offset array (uint32_t like C)
row_offsets = np.zeros(height + 1, dtype=np.uint32)
# Encoding data buffer (uint32_t for index calculations)
encoded_data = np.zeros(int(max_output_size), dtype=np.uint32)
encoded_index = np.uint32(0)
# Traverse row by row (uint16_t y in C)
for y in range(int(height)):
row_offsets[y] = encoded_index
row_start = y * int(width)
row = input_data[row_start:row_start + int(width)]
col = np.uint32(0)
while col < width:
# Check RLE length (uint32_t in C)
rle_len = check_rle_length(row, col, width)
if rle_len >= RLE_THRESHOLD:
color = np.uint16(row[int(col)])
if rle_len >= np.uint32(128):
# Long encoding: flag, color, count (match C exactly)
encoded_data[int(encoded_index)] = encode_flag
encoded_index += np.uint32(1)
encoded_data[int(encoded_index)] = color
encoded_index += np.uint32(1)
encoded_data[int(encoded_index)] = rle_len
encoded_index += np.uint32(1)
else:
# Short encoding: flag + count, color (match C exactly)
encoded_data[int(encoded_index)] = np.uint16(encode_flag + rle_len)
encoded_index += np.uint32(1)
encoded_data[int(encoded_index)] = color
encoded_index += np.uint32(1)
col += rle_len
else:
# Handle pixels that conflict with flag
color_tmp = np.uint16(row[int(col)])
if (color_tmp & np.uint16(0xFF00)) == encode_flag:
# Pixel conflicts with flag code, use RLE short encoding to store single pixel
encoded_data[int(encoded_index)] = np.uint16(encode_flag + 1)
encoded_index += np.uint32(1)
encoded_data[int(encoded_index)] = color_tmp
encoded_index += np.uint32(1)
else:
# Store original pixel directly
encoded_data[int(encoded_index)] = color_tmp
encoded_index += np.uint32(1)
col += np.uint32(1)
row_offsets[height] = encoded_index
# Calculate upgrade table - match C logic exactly (uint16_t upgrade[500])
upgrade = np.zeros(500, dtype=np.uint16)
upgrade_len = np.uint16(0)
for i in range(int(height) - 1):
tmp0 = row_offsets[i]
tmp1 = row_offsets[i + 1]
if tmp0 > tmp1:
upgrade[int(upgrade_len)] = np.uint16(i + 1)
upgrade_len += np.uint16(1)
# Calculate row table start coordinate and encoding data start coordinate - match C exactly
row_offset_addr = np.uint16(6) + upgrade_len
encode_data_addr = row_offset_addr + np.uint16(height) + np.uint16(1)
# Fill header - match C structure exactly
output[0] = width
output[1] = height
output[2] = encode_flag
output[3] = upgrade_len
output[4] = row_offset_addr
output[5] = encode_data_addr
idx = np.uint32(6)
# Write upgrade table (uint16_t values)
if upgrade_len > 0:
for i in range(int(upgrade_len)):
output[int(idx)] = upgrade[i]
idx += np.uint32(1)
# Write row offset table (uint32_t values)
for i in range(int(height) + 1):
output[int(idx)] = row_offsets[i]
idx += np.uint32(1)
# Write encoding data
for i in range(int(encoded_index)):
output[int(idx)] = encoded_data[i]
idx += np.uint32(1)
# Calculate compression ratio - match C calculation exactly
original_size = float(pixel_count * np.uint64(2)) # Original data size (bytes)
compressed_size = float(idx * np.uint32(2)) # Compressed size (bytes)
compression_ratio = (compressed_size / original_size) * 100.0
# Convert to uint16 array for return (like C output)
result = output[:int(idx)].astype(np.uint16)
return result, idx, compression_ratio
def encode_rgb565_rle_diff(input_data: np.ndarray, width: np.uint16, height: np.uint16) -> Tuple[Optional[np.ndarray], np.uint32, float]:
"""
RLE+Differential mixed encoding function - strictly matches C implementation data types
Args:
input_data: Input RGB565 data, length is width*height
width: Image width (uint16_t in C)
height: Image height (uint16_t in C)
Returns:
(output_data, output_size, compression_ratio)
"""
pixel_count = np.uint64(width) * np.uint64(height)
if width == np.uint16(0) or height == np.uint16(0) or pixel_count == np.uint64(0):
return None, np.uint32(0), 0.0
# Find encoding flag - match C call exactly (uint64_t pixel_count)
encode_flag, encode_flag_cs, encode_flag_mode, flag_ok = find_encode_flag(input_data, pixel_count)
# Estimate maximum output size - match C calculation exactly (uint64_t)
max_output_size = np.uint64(6) + np.uint64(height) + (pixel_count * np.uint64(2))
output = np.zeros(int(max_output_size), dtype=np.uint32) # Use 32-bit to avoid overflow
# Allocate row offset array - use uint32_t like C
row_offsets = np.zeros(int(height) + 1, dtype=np.uint32)
# Differential encoding data buffer - match C: encoded_diff_data = (uint16_t*)malloc((size_t)65536 * 2 * sizeof(uint16_t))
encoded_diff_data = np.zeros(65536 * 2, dtype=np.uint16)
# Encoding data buffer - use uint32_t for index calculations (encoded_index = uint32_t)
encoded_data = np.zeros(int(max_output_size), dtype=np.uint32)
encoded_index = np.uint32(0)
# Traverse row by row - match C logic exactly (uint16_t y)
for y in range(int(height)):
row_offsets[y] = encoded_index
row_start = y * int(width)
row = input_data[row_start:row_start + int(width)]
col = np.uint32(0)
while col < width:
# Try differential encoding first (like C) - uint32_t parameters
diff_len = calculate_diff_length(row, col, width, encoded_diff_data)
if diff_len >= np.uint16(DIFF_THRESHOLD):
base_color = np.uint16(row[int(col)])
if diff_len >= np.uint16(128):
# Long encoding - match C exactly
encoded_data[int(encoded_index)] = encode_flag
encoded_index += np.uint32(1)
encoded_data[int(encoded_index)] = base_color
encoded_index += np.uint32(1)
encoded_data[int(encoded_index)] = np.uint16(0x8000 + (diff_len // np.uint16(2)))
encoded_index += np.uint32(1)
for i in range(int(diff_len) // 2):
encoded_data[int(encoded_index)] = encoded_diff_data[i]
encoded_index += np.uint32(1)
else:
# Short encoding - match C exactly
encoded_data[int(encoded_index)] = np.uint16(encode_flag + np.uint16(0x80) + (diff_len // np.uint16(2)))
encoded_index += np.uint32(1)
encoded_data[int(encoded_index)] = base_color
encoded_index += np.uint32(1)
for i in range(int(diff_len) // 2):
encoded_data[int(encoded_index)] = encoded_diff_data[i]
encoded_index += np.uint32(1)
col += diff_len
else:
# Try RLE encoding (like C) - uint32_t parameters
rle_len = check_rle_length(row, col, width)
if rle_len >= np.uint32(RLE_THRESHOLD):
color = np.uint16(row[int(col)])
if rle_len >= np.uint32(128):
# Long encoding - match C exactly
encoded_data[int(encoded_index)] = encode_flag
encoded_index += np.uint32(1)
encoded_data[int(encoded_index)] = color
encoded_index += np.uint32(1)
encoded_data[int(encoded_index)] = rle_len
encoded_index += np.uint32(1)
else:
# Short encoding - match C exactly
encoded_data[int(encoded_index)] = np.uint16(encode_flag + rle_len)
encoded_index += np.uint32(1)
encoded_data[int(encoded_index)] = color
encoded_index += np.uint32(1)
col += rle_len
else:
# Store original pixel directly - match C logic exactly
color_tmp = np.uint16(row[int(col)])
if (color_tmp & np.uint16(0xFF00)) == encode_flag:
# Pixel conflicts with flag code - match C exactly
encoded_data[int(encoded_index)] = np.uint16(encode_flag + np.uint16(1))
encoded_index += np.uint32(1)
encoded_data[int(encoded_index)] = row[int(col)] # CRITICAL: C uses row[col], not color_tmp
encoded_index += np.uint32(1)
else:
# Store original pixel directly
encoded_data[int(encoded_index)] = color_tmp
encoded_index += np.uint32(1)
col += np.uint32(1)
row_offsets[int(height)] = encoded_index
# Calculate upgrade table - match C logic exactly (uint16_t upgrade[500])
upgrade = np.zeros(500, dtype=np.uint16)
upgrade_len = np.uint16(0)
for i in range(int(height) - 1):
# CRITICAL: C casts to uint16_t for comparison
tmp0 = np.uint16(row_offsets[i])
tmp1 = np.uint16(row_offsets[i + 1])
if tmp0 > tmp1:
upgrade[int(upgrade_len)] = np.uint16(i + 1)
upgrade_len += np.uint16(1)
# Calculate row table start coordinate and encoding data start coordinate - match C exactly
row_offset_addr = np.uint16(6) + upgrade_len
encode_data_addr = row_offset_addr + np.uint16(height) + np.uint16(1)
# Fill header - match C structure exactly
output[0] = width
output[1] = height
output[2] = encode_flag
output[3] = upgrade_len
output[4] = row_offset_addr
output[5] = encode_data_addr
idx = np.uint32(6)
# Write upgrade table (uint16_t values)
if upgrade_len > 0:
for i in range(int(upgrade_len)):
output[int(idx)] = upgrade[i]
idx += np.uint32(1)
# Write row offset table (uint16_t values - CRITICAL: C casts to uint16_t)
for i in range(int(height) + 1):
output[int(idx)] = np.uint16(row_offsets[i])
idx += np.uint32(1)
# Write encoding data
for i in range(int(encoded_index)):
output[int(idx)] = encoded_data[i]
idx += np.uint32(1)
# Calculate compression ratio - match C calculation exactly
original_size = float(pixel_count * np.uint64(2))
compressed_size = float(idx * np.uint32(2))
compression_ratio = (compressed_size / original_size) * 100.0
# Convert to uint16 array for return (like C output)
result = output[:int(idx)].astype(np.uint16)
return result, idx, compression_ratio
def generate_c_array(output_data: np.ndarray, output_size: np.uint32,
width: np.uint16, height: np.uint16, compression_ratio: float,
src_path: str = "", array_name: str = "img") -> str:
"""
Generate C language array format string - matches C implementation
Args:
output_data: Encoded data
output_size: Data size (uint32_t in C)
width: Image width (uint16_t in C)
height: Image height (uint16_t in C)
compression_ratio: Compression ratio
src_path: Source file path
array_name: Array name
Returns:
C language code string
"""
import os
# Get base filename
base_name = os.path.splitext(os.path.basename(src_path))[0] if src_path else "image"
lines = []
lines.append("// Compressed RGB565 data")
lines.append(f"// Source file: {src_path}")
lines.append(f"// Original size: {width} x {height} = {width * height} pixels")
lines.append(f"// Compression ratio: {compression_ratio:.2f}%")
lines.append("")
lines.append(f"const uint16_t _{base_name}_zhRGB565_Data[{output_size}] = {{")
lines.append("")
# Write header information
lines.append(" /* width, height, encode_flag, level_up_table_len, row_offset_addr, data_addr */")
lines.append(f" {output_data[0]}, {output_data[1]}, 0x{output_data[2]:04X}, {output_data[3]}, {output_data[4]}, {output_data[5]},")
lines.append("")
idx = np.uint32(6)
upgrade_len = np.uint16(output_data[3])
row_offset_addr = np.uint16(output_data[4])
# Write upgrade table
if upgrade_len > 0:
lines.append(" /* level_up table */")
line = " "
count = 0
for i in range(int(upgrade_len)):
line += f"{output_data[int(idx)]}, "
idx += np.uint32(1)
count += 1
if count % 16 == 0:
lines.append(line.rstrip())
line = " "
if line.strip():
lines.append(line.rstrip())
lines.append("")
else:
lines.append(" /* NO level_up table */")
lines.append("")
# Write row offset table
lines.append(f" /* Row offset table ({height} rows total) */")
line = " "
for i in range(int(height) + 1):
line += f"{output_data[int(idx)]}"
idx += np.uint32(1)
line += ", " # Add comma after all elements (including last)
if (i + 1) % 16 == 0:
lines.append(line.rstrip())
line = " "
if line.strip():
lines.append(line.rstrip())
lines.append("")
# Write encoding data
lines.append(" /* Encoded data */")
# Format output by lines - match C logic exactly
hhcnt = np.uint32(0)
line_base = np.uint32(0)
next_line = np.uint32(1)
current_line = np.uint32(0)
lines.append(f" /* 0 */")
line = " "
while idx < output_size:
if next_line == height:
# Last line, output all
while idx < output_size:
line += f"0x{int(output_data[int(idx)]):04X}, "
idx += np.uint32(1)
hhcnt += np.uint32(1)
if hhcnt % 16 == 0:
lines.append(line.rstrip())
line = " "
else:
current_line_pos = np.uint32(output_data[int(row_offset_addr) + int(current_line)]) + line_base
next_line_pos = np.uint32(output_data[int(row_offset_addr) + int(next_line)]) + line_base
if current_line_pos > next_line_pos:
line_base += np.uint32(65536)
next_line_pos += np.uint32(65536)
for j in range(int(current_line_pos), int(next_line_pos)):
if idx >= output_size:
break
line += f"0x{int(output_data[int(idx)]):04X}, "
idx += np.uint32(1)
hhcnt += np.uint32(1)
if hhcnt % 16 == 0 and j != int(next_line_pos) - 1:
lines.append(line.rstrip())
line = " "
current_line = next_line
next_line += np.uint32(1)
hhcnt = np.uint32(0)
if idx >= output_size:
break
if line.strip():
lines.append(line.rstrip())
lines.append(f" /* {current_line} */")
line = " "
if line.strip():
lines.append(line.rstrip())
lines.append("};")
lines.append("")
return "\n".join(lines)
if __name__ == "__main__":
# Test code
# Create a simple test image
test_data = np.array([
0xF800, 0xF800, 0xF800, 0x07E0, 0x07E0, 0x001F, 0x001F, 0x001F,
0xF800, 0xF800, 0xF800, 0x07E0, 0x07E0, 0x001F, 0x001F, 0x001F,
], dtype=np.uint16)
width = np.uint16(8)
height = np.uint16(2)
print("Testing RLE only encoding:")
result, size, ratio = encode_rgb565_rle_only(test_data, width, height)
if result is not None:
print(f"Encoding successful!")
print(f"Output size: {size} uint16")
print(f"Compression ratio: {ratio:.2f}%")
print("\nGenerated C array:")
c_code = generate_c_array(result, size, width, height, ratio, "test.bmp")
print(c_code)
print("\n" + "="*50 + "\n")
# Test RLE+Diff encoding
print("Testing RLE+Diff encoding:")
result, size, ratio = encode_rgb565_rle_diff(test_data, width, height)
if result is not None:
print(f"Encoding successful!")
print(f"Output size: {size} uint16")
print(f"Compression ratio: {ratio:.2f}%")
print("\nGenerated C array:")
c_code = generate_c_array(result, size, width, height, ratio, "test.bmp")
print(c_code)