Add RGB565 and RGB888 color support to Quantum Painter (#19382)
This commit is contained in:
		
							parent
							
								
									5873fbe569
								
							
						
					
					
						commit
						45851a10f6
					
				
					 21 changed files with 226 additions and 43 deletions
				
			
		| 
						 | 
				
			
			@ -7,6 +7,20 @@ from PIL import Image, ImageOps
 | 
			
		|||
 | 
			
		||||
# The list of valid formats Quantum Painter supports
 | 
			
		||||
valid_formats = {
 | 
			
		||||
    'rgb888': {
 | 
			
		||||
        'image_format': 'IMAGE_FORMAT_RGB888',
 | 
			
		||||
        'bpp': 24,
 | 
			
		||||
        'has_palette': False,
 | 
			
		||||
        'num_colors': 16777216,
 | 
			
		||||
        'image_format_byte': 0x09,  # see qp_internal_formats.h
 | 
			
		||||
    },
 | 
			
		||||
    'rgb565': {
 | 
			
		||||
        'image_format': 'IMAGE_FORMAT_RGB565',
 | 
			
		||||
        'bpp': 16,
 | 
			
		||||
        'has_palette': False,
 | 
			
		||||
        'num_colors': 65536,
 | 
			
		||||
        'image_format_byte': 0x08,  # see qp_internal_formats.h
 | 
			
		||||
    },
 | 
			
		||||
    'pal256': {
 | 
			
		||||
        'image_format': 'IMAGE_FORMAT_PALETTE',
 | 
			
		||||
        'bpp': 8,
 | 
			
		||||
| 
						 | 
				
			
			@ -144,19 +158,33 @@ def convert_requested_format(im, format):
 | 
			
		|||
    ncolors = format["num_colors"]
 | 
			
		||||
    image_format = format["image_format"]
 | 
			
		||||
 | 
			
		||||
    # Ensure we have a valid number of colors for the palette
 | 
			
		||||
    if ncolors <= 0 or ncolors > 256 or (ncolors & (ncolors - 1) != 0):
 | 
			
		||||
        raise ValueError("Number of colors must be 2, 4, 16, or 256.")
 | 
			
		||||
 | 
			
		||||
    # Work out where we're getting the bytes from
 | 
			
		||||
    if image_format == 'IMAGE_FORMAT_GRAYSCALE':
 | 
			
		||||
        # Ensure we have a valid number of colors for the palette
 | 
			
		||||
        if ncolors <= 0 or ncolors > 256 or (ncolors & (ncolors - 1) != 0):
 | 
			
		||||
            raise ValueError("Number of colors must be 2, 4, 16, or 256.")
 | 
			
		||||
        # If mono, convert input to grayscale, then to RGB, then grab the raw bytes corresponding to the intensity of the red channel
 | 
			
		||||
        im = ImageOps.grayscale(im)
 | 
			
		||||
        im = im.convert("RGB")
 | 
			
		||||
    elif image_format == 'IMAGE_FORMAT_PALETTE':
 | 
			
		||||
        # Ensure we have a valid number of colors for the palette
 | 
			
		||||
        if ncolors <= 0 or ncolors > 256 or (ncolors & (ncolors - 1) != 0):
 | 
			
		||||
            raise ValueError("Number of colors must be 2, 4, 16, or 256.")
 | 
			
		||||
        # If color, convert input to RGB, palettize based on the supplied number of colors, then get the raw palette bytes
 | 
			
		||||
        im = im.convert("RGB")
 | 
			
		||||
        im = im.convert("P", palette=Image.ADAPTIVE, colors=ncolors)
 | 
			
		||||
    elif image_format == 'IMAGE_FORMAT_RGB565':
 | 
			
		||||
        # Ensure we have a valid number of colors for the palette
 | 
			
		||||
        if ncolors != 65536:
 | 
			
		||||
            raise ValueError("Number of colors must be 65536.")
 | 
			
		||||
        # If color, convert input to RGB
 | 
			
		||||
        im = im.convert("RGB")
 | 
			
		||||
    elif image_format == 'IMAGE_FORMAT_RGB888':
 | 
			
		||||
        # Ensure we have a valid number of colors for the palette
 | 
			
		||||
        if ncolors != 1677216:
 | 
			
		||||
            raise ValueError("Number of colors must be 16777216.")
 | 
			
		||||
        # If color, convert input to RGB
 | 
			
		||||
        im = im.convert("RGB")
 | 
			
		||||
 | 
			
		||||
    return im
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -170,8 +198,12 @@ def convert_image_bytes(im, format):
 | 
			
		|||
    image_format = format["image_format"]
 | 
			
		||||
    shifter = int(math.log2(ncolors))
 | 
			
		||||
    pixels_per_byte = int(8 / math.log2(ncolors))
 | 
			
		||||
    bytes_per_pixel = math.ceil(math.log2(ncolors) / 8)
 | 
			
		||||
    (width, height) = im.size
 | 
			
		||||
    expected_byte_count = ((width * height) + (pixels_per_byte - 1)) // pixels_per_byte
 | 
			
		||||
    if (pixels_per_byte != 0):
 | 
			
		||||
        expected_byte_count = ((width * height) + (pixels_per_byte - 1)) // pixels_per_byte
 | 
			
		||||
    else:
 | 
			
		||||
        expected_byte_count = width * height * bytes_per_pixel
 | 
			
		||||
 | 
			
		||||
    if image_format == 'IMAGE_FORMAT_GRAYSCALE':
 | 
			
		||||
        # Take the red channel
 | 
			
		||||
| 
						 | 
				
			
			@ -212,6 +244,44 @@ def convert_image_bytes(im, format):
 | 
			
		|||
                    byte = byte | ((image_bytes[byte_offset] & (ncolors - 1)) << int(n * shifter))
 | 
			
		||||
            bytearray.append(byte)
 | 
			
		||||
 | 
			
		||||
    if image_format == 'IMAGE_FORMAT_RGB565':
 | 
			
		||||
        # Take the red, green, and blue channels
 | 
			
		||||
        image_bytes_red = im.tobytes("raw", "R")
 | 
			
		||||
        image_bytes_green = im.tobytes("raw", "G")
 | 
			
		||||
        image_bytes_blue = im.tobytes("raw", "B")
 | 
			
		||||
        image_pixels_len = len(image_bytes_red)
 | 
			
		||||
 | 
			
		||||
        # No palette
 | 
			
		||||
        palette = None
 | 
			
		||||
 | 
			
		||||
        bytearray = []
 | 
			
		||||
        for x in range(image_pixels_len):
 | 
			
		||||
            # 5 bits of red, 3 MSb of green
 | 
			
		||||
            byte = ((image_bytes_red[x] >> 3 & 0x1F) << 3) + (image_bytes_green[x] >> 5 & 0x07)
 | 
			
		||||
            bytearray.append(byte)
 | 
			
		||||
            # 3 LSb of green, 5 bits of blue
 | 
			
		||||
            byte = ((image_bytes_green[x] >> 2 & 0x07) << 5) + (image_bytes_blue[x] >> 3 & 0x1F)
 | 
			
		||||
            bytearray.append(byte)
 | 
			
		||||
 | 
			
		||||
    if image_format == 'IMAGE_FORMAT_RGB888':
 | 
			
		||||
        # Take the red, green, and blue channels
 | 
			
		||||
        image_bytes_red = im.tobytes("raw", "R")
 | 
			
		||||
        image_bytes_green = im.tobytes("raw", "G")
 | 
			
		||||
        image_bytes_blue = im.tobytes("raw", "B")
 | 
			
		||||
        image_pixels_len = len(image_bytes_red)
 | 
			
		||||
 | 
			
		||||
        # No palette
 | 
			
		||||
        palette = None
 | 
			
		||||
 | 
			
		||||
        bytearray = []
 | 
			
		||||
        for x in range(image_pixels_len):
 | 
			
		||||
            byte = image_bytes_red[x]
 | 
			
		||||
            bytearray.append(byte)
 | 
			
		||||
            byte = image_bytes_green[x]
 | 
			
		||||
            bytearray.append(byte)
 | 
			
		||||
            byte = image_bytes_blue[x]
 | 
			
		||||
            bytearray.append(byte)
 | 
			
		||||
 | 
			
		||||
    if len(bytearray) != expected_byte_count:
 | 
			
		||||
        raise Exception(f"Wrong byte count, was {len(bytearray)}, expected {expected_byte_count}")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue