mirror of
https://git.planet-casio.com/Lephenixnoir/fxsdk.git
synced 2024-12-29 13:03:37 +01:00
fxconv: rewrite image converter, forcing alpha value
This commit is contained in:
parent
58cb14157d
commit
b29c494715
1 changed files with 161 additions and 235 deletions
396
fxconv/fxconv.py
396
fxconv/fxconv.py
|
@ -90,40 +90,50 @@ FX_PROFILES = [
|
||||||
|
|
||||||
# fx-CG 50 profiles
|
# fx-CG 50 profiles
|
||||||
class CgProfile:
|
class CgProfile:
|
||||||
def __init__(self, id, name, alpha):
|
def __init__(self, id, depth, names, alpha=None, palette=None):
|
||||||
"""
|
# Numerical ID
|
||||||
Construct a CgProfile object.
|
|
||||||
* [id] is the profile ID in bopti
|
|
||||||
* [name] is the profile name as found in the specification key
|
|
||||||
* [alpha] is True if this profile supports alpha, False otherwise
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.id = id
|
self.id = id
|
||||||
self.name = name
|
# Name for printing
|
||||||
self.supports_alpha = alpha
|
self.names = names
|
||||||
|
# Bit depth
|
||||||
|
self.depth = depth
|
||||||
|
# Whether the profile has alpha
|
||||||
|
self.has_alpha = (alpha is not None)
|
||||||
|
# Alpha value (None has_alpha == False)
|
||||||
|
self.alpha = alpha
|
||||||
|
# Whether the profile is indexed
|
||||||
|
self.is_indexed = (palette is not None)
|
||||||
|
|
||||||
|
if palette is not None:
|
||||||
|
# Palette base value (skipping the alpha value)
|
||||||
|
self.palette_base = palette[0]
|
||||||
|
# Color count (indices are always 0..color_count-1, wraps around)
|
||||||
|
self.color_count = palette[1]
|
||||||
|
# Whether to trim the palette to a minimal length
|
||||||
|
self.trim_palette = palette[2]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def find(name):
|
def find(name):
|
||||||
"""Find a profile by name."""
|
"""Find a profile by name."""
|
||||||
for profile in CG_PROFILES:
|
for profile in CG_PROFILES:
|
||||||
if profile.name == name:
|
if name in profile.names:
|
||||||
return profile
|
return profile
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
IMAGE_RGB16 = 0
|
||||||
|
IMAGE_P8 = 1
|
||||||
|
IMAGE_P4 = 2
|
||||||
|
|
||||||
CG_PROFILES = [
|
CG_PROFILES = [
|
||||||
# 16-bit RGB565 and RGB565 with alpha
|
# 16-bit RGB565 and RGB565 with alpha
|
||||||
CgProfile(0x0, "rgb565", False),
|
CgProfile(0x0, IMAGE_RGB16, ["rgb565", "r5g6b5"]),
|
||||||
CgProfile(0x1, "rgb565a", True),
|
CgProfile(0x1, IMAGE_RGB16, ["rgb565a", "r5g6b5a"], alpha=0x0001),
|
||||||
# 8-bit palette for RGB565 and RGB565A
|
# 8-bit palette for RGB565 and RGB565A
|
||||||
CgProfile(0x4, "p8_rgb565", False),
|
CgProfile(0x4, IMAGE_P8, "p8_rgb565", palette=(0x80,256,True)),
|
||||||
CgProfile(0x5, "p8_rgb565a", True),
|
CgProfile(0x5, IMAGE_P8, "p8_rgb565a", alpha=0x80, palette=(0x81,256,True)),
|
||||||
# 4-bit palette for RGB565 and RGB565A
|
# 4-bit palette for RGB565 and RGB565A
|
||||||
CgProfile(0x6, "p4_rgb565", False),
|
CgProfile(0x6, IMAGE_P4, "p4_rgb565", palette=(0,16,False)),
|
||||||
CgProfile(0x3, "p4_rgb565a", True),
|
CgProfile(0x3, IMAGE_P4, "p4_rgb565a", alpha=0, palette=(1,16,False)),
|
||||||
|
|
||||||
# Original names for RGB565 and RGB565A
|
|
||||||
CgProfile(0x0, "r5g6b5", False),
|
|
||||||
CgProfile(0x1, "r5g6b5a", True),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# Libimg flags
|
# Libimg flags
|
||||||
|
@ -511,7 +521,19 @@ def _image_project(img, f):
|
||||||
# Image conversion for fx-CG 50
|
# Image conversion for fx-CG 50
|
||||||
#
|
#
|
||||||
|
|
||||||
|
def image_has_alpha(img):
|
||||||
|
# Convert the alpha channel to 1-bit; check if there are transparent pixels
|
||||||
|
try:
|
||||||
|
alpha_channel = img.getchannel("A").convert("1", dither=Image.NONE)
|
||||||
|
alpha_levels = { t[1]: t[0] for t in alpha_channel.getcolors() }
|
||||||
|
return 0 in alpha_levels
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
|
||||||
def convert_bopti_cg(input, params):
|
def convert_bopti_cg(input, params):
|
||||||
|
return convert_image_cg(input, params)
|
||||||
|
|
||||||
|
def convert_image_cg(input, params):
|
||||||
if isinstance(input, Image.Image):
|
if isinstance(input, Image.Image):
|
||||||
img = input.copy()
|
img = input.copy()
|
||||||
else:
|
else:
|
||||||
|
@ -523,68 +545,48 @@ def convert_bopti_cg(input, params):
|
||||||
area = Area(params.get("area", {}), img)
|
area = Area(params.get("area", {}), img)
|
||||||
img = img.crop(area.tuple())
|
img = img.crop(area.tuple())
|
||||||
|
|
||||||
# If no profile is specified, fall back to rgb565 or rgb565a later on
|
img = img.convert("RGBA")
|
||||||
name = params.get("profile", None)
|
|
||||||
if name is not None:
|
|
||||||
profile = CgProfile.find(name)
|
|
||||||
|
|
||||||
if name in ["r5g6b5", "r5g6b5a", "rgb565", "rgb565a", None]:
|
#---
|
||||||
# Encode the image into the 16-bit format
|
# Format selection
|
||||||
encoded, stride, alpha = r5g6b5(img)
|
#---
|
||||||
|
|
||||||
if name is None:
|
format_name = params.get("profile", "")
|
||||||
name = "rgb565" if alpha is None else "rgb565a"
|
has_alpha = image_has_alpha(img)
|
||||||
profile = CgProfile.find(name)
|
|
||||||
|
|
||||||
color_count = -1
|
# If no format is specified, select rgb565 or rgb565a
|
||||||
palette = None
|
if format_name == "":
|
||||||
|
format_name = "rgb565a" if has_alpha else "rgb565"
|
||||||
|
# Similarly, if "p8" or "p4" is specified, select the cheapest variation
|
||||||
|
elif format_name == "p8":
|
||||||
|
format_name = "p8_rgb565a" if has_alpha else "p8_rgb565"
|
||||||
|
elif format_name == "p4":
|
||||||
|
format_name = "p4_rgb565a" if has_alpha else "p4_rgb565"
|
||||||
|
|
||||||
elif name.startswith("p"):
|
# Otherwise, just use the format as specified
|
||||||
force_alpha = name.endswith("_rgb565a")
|
format = CgProfile.find(format_name)
|
||||||
if name.startswith("p8"):
|
if format is None:
|
||||||
trim_palette = True
|
raise FxconvError(f"unknown image format '{format_name}")
|
||||||
palette_base = 0x80
|
|
||||||
color_count = 256
|
|
||||||
elif name.startswith("p4"):
|
|
||||||
trim_palette = False
|
|
||||||
palette_base = 0x00
|
|
||||||
color_count = 16
|
|
||||||
else:
|
|
||||||
raise FxconvError(f"unknown palette format {profile}")
|
|
||||||
|
|
||||||
# Encode the image into 16-bit with a palette of 16 or 256 entries
|
# Check that we have transparency support if we need it
|
||||||
encoded, stride, palette, alpha = r5g6b5(img, color_count=color_count,
|
if has_alpha and not format.has_alpha:
|
||||||
trim_palette=trim_palette, palette_base=palette_base,
|
raise FxconvError(f"'{input}' has transparency, which {format_name} "+
|
||||||
force_alpha=force_alpha)
|
"doesn't support")
|
||||||
|
|
||||||
color_count = len(palette) // 2
|
#---
|
||||||
|
# Structure generation
|
||||||
|
#---
|
||||||
|
|
||||||
else:
|
data, stride, palette, color_count = image_encode(img, format)
|
||||||
raise FxconvError(f"unknown color profile '{name}'")
|
|
||||||
|
|
||||||
# Resolve "p8" and "p4" to their optimal variation
|
|
||||||
if name == "p8":
|
|
||||||
name = "p8_rgb565" if alpha is None else "p8_rgb565a"
|
|
||||||
profile = CgProfile.find(name)
|
|
||||||
elif name == "p4":
|
|
||||||
name = "p4_rgb565" if alpha is None else "p4_rgb565a"
|
|
||||||
profile = CgProfile.find(name)
|
|
||||||
|
|
||||||
if alpha is not None and not profile.supports_alpha:
|
|
||||||
raise FxconvError(f"'{input}' has transparency; use rgb565a, " +
|
|
||||||
"p8_rgb565a or p4_rgb565a")
|
|
||||||
|
|
||||||
if len(encoded) % 4 != 0:
|
|
||||||
encoded += bytes(4 - (len(encoded) % 4))
|
|
||||||
|
|
||||||
o = ObjectData()
|
o = ObjectData()
|
||||||
o += u8(profile.id)
|
o += u8(format.id)
|
||||||
o += u8(3) # DATA_RO, PALETTE_RO
|
o += u8(3) # DATA_RO, PALETTE_RO
|
||||||
o += u16(alpha if alpha is not None else 0xffff)
|
o += u16(format.alpha if format.alpha is not None else 0xffff)
|
||||||
o += u16(img.width)
|
o += u16(img.width)
|
||||||
o += u16(img.height)
|
o += u16(img.height)
|
||||||
o += u32(stride)
|
o += u32(stride)
|
||||||
o += ref(encoded, padding=4)
|
o += ref(data, padding=4)
|
||||||
o += u32(color_count)
|
o += u32(color_count)
|
||||||
if palette is None:
|
if palette is None:
|
||||||
o += u32(0)
|
o += u32(0)
|
||||||
|
@ -856,7 +858,8 @@ def convert_libimg_cg(input, params):
|
||||||
img = img.crop(area.tuple())
|
img = img.crop(area.tuple())
|
||||||
|
|
||||||
# Encode the image into 16-bit format and force the alpha to 0x0001
|
# Encode the image into 16-bit format and force the alpha to 0x0001
|
||||||
encoded, stride, alpha = r5g6b5(img, force_alpha=(0x0001,0x0000))
|
format = CgProfile.find("rgb565a")
|
||||||
|
encoded, stride, palette, color_count = image_encode(img, format)
|
||||||
|
|
||||||
o = ObjectData()
|
o = ObjectData()
|
||||||
o += u16(img.width) + u16(img.height)
|
o += u16(img.width) + u16(img.height)
|
||||||
|
@ -942,218 +945,141 @@ def quantize(img, dither=False):
|
||||||
|
|
||||||
return img
|
return img
|
||||||
|
|
||||||
def r5g6b5(img, color_count=0, trim_palette=False, palette_base=0,
|
def image_encode(img, format):
|
||||||
force_alpha=None):
|
|
||||||
"""
|
"""
|
||||||
Convert a PIL.Image.Image into an RGB565 byte stream. If there are
|
Encodes a PIL.Image.Image into one of the fx-CG image formats. The color
|
||||||
transparent pixels, chooses a color to implement alpha and replaces them
|
depth is either RGB16, P8 or P4, with various transparency settings and
|
||||||
with this color.
|
palette encodings.
|
||||||
|
|
||||||
When color_count=0, converts the image to 16-bit; returns a bytearray of
|
Returns 4 values:
|
||||||
pixel data, the byte stride, and the automatically-chosen alpha value (or
|
* data: A bytearray containing the encoded image data
|
||||||
None).
|
* stride: The byte stride of the data array
|
||||||
|
* palette: A bytearray containing the encoded palette (None if not indexed)
|
||||||
* If force_alpha is a pair (value, replacement), then the alpha is forced
|
* color_count: Number of colors in the palette (-1 if not indexed)
|
||||||
to be the indicated value, and any natural occurrence of the value is
|
|
||||||
substituted with the replacement.
|
|
||||||
|
|
||||||
When color_count>0, if should be either 16 or 256. The image is encoded
|
|
||||||
with a palette of that size. Returns the converted image as a bytearray,
|
|
||||||
the byte stride, the palette as a bytearray, and the alpha value (None if
|
|
||||||
there were no transparent pixels).
|
|
||||||
|
|
||||||
* If trim_palette is set, the palette bytearray is trimmed so that only
|
|
||||||
used entries are set (this does not include alpha, which is at the end).
|
|
||||||
* If palette_base is provided, palette entries will be numbered starting
|
|
||||||
from that value, wrapping around modulo color_count.
|
|
||||||
* If force_alpha=True, an index will be reserved for alpha even if no pixel
|
|
||||||
is transparent in the source image.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def rgb24to16(rgb24):
|
|
||||||
r, g, b = rgb24
|
|
||||||
r = (r & 0xff) >> 3
|
|
||||||
g = (g & 0xff) >> 2
|
|
||||||
b = (b & 0xff) >> 3
|
|
||||||
return (r << 11) | (g << 5) | b
|
|
||||||
|
|
||||||
#---
|
#---
|
||||||
# Initial image transforms
|
# Separate the alpha channel
|
||||||
# Separate the alpha channel and generate a first palette.
|
|
||||||
#---
|
#---
|
||||||
|
|
||||||
# Save the alpha channel and make it 1-bit. If there are transparent
|
# Save the alpha channel and make it 1-bit. If there are transparent
|
||||||
# pixels, set has_alpha=True and record the alpha channel in alpha_pixels.
|
# pixels, set has_alpha=True and record the alpha channel in alpha_pixels.
|
||||||
try:
|
if format.has_alpha:
|
||||||
alpha_channel = img.getchannel("A").convert("1", dither=Image.NONE)
|
alpha_channel = img.getchannel("A").convert("1", dither=Image.NONE)
|
||||||
alpha_levels = { t[1]: t[0] for t in alpha_channel.getcolors() }
|
else:
|
||||||
has_alpha = 0 in alpha_levels
|
alpha_channel = Image.new("1", img.size, 1)
|
||||||
replacement = None
|
|
||||||
|
|
||||||
if has_alpha:
|
alpha_pixels = alpha_channel.load()
|
||||||
alpha_pixels = alpha_channel.load()
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
has_alpha = False
|
|
||||||
|
|
||||||
# Convert the input image to RGB
|
|
||||||
img = img.convert("RGB")
|
img = img.convert("RGB")
|
||||||
|
|
||||||
# Transparent pixels also have values on the RGB channels, so they use up a
|
# Transparent pixels have random values on the RGB channels, causing them
|
||||||
# palette entry (in indexed mode) or possible alpha value (in 16-bit mode)
|
# to use up palette entries during quantization. To avoid that, set their
|
||||||
# even though their color is unused. Replace them with a non-transparent
|
# RGB data to a color used somewhere else in the image.
|
||||||
# color used elsewhere in the image to avoid that.
|
pixels = img.load()
|
||||||
if has_alpha:
|
bg_color = next((pixels[x,y]
|
||||||
nontransparent_pixels = { (x,y)
|
for x in range(img.width) for y in range(img.height)
|
||||||
for x in range(img.width) for y in range(img.height)
|
if alpha_pixels[x,y] > 0),
|
||||||
if alpha_pixels[x,y] > 0 }
|
(0,0,0))
|
||||||
|
|
||||||
if nontransparent_pixels:
|
for y in range(img.height):
|
||||||
x0, y0 = nontransparent_pixels.pop()
|
for x in range(img.width):
|
||||||
pixels = img.load()
|
if alpha_pixels[x,y] == 0:
|
||||||
|
pixels[x,y] = bg_color
|
||||||
|
|
||||||
for y in range(img.height):
|
#---
|
||||||
for x in range(img.width):
|
# Quantize to generate a palette
|
||||||
if alpha_pixels[x,y] == 0:
|
#---
|
||||||
pixels[x,y] = pixels[x0,y0]
|
|
||||||
|
|
||||||
# In indexed mode, generate a specific palette
|
if format.is_indexed:
|
||||||
if color_count:
|
palette_max_size = format.color_count - int(format.has_alpha)
|
||||||
palette_max_size = color_count - int(has_alpha or force_alpha == True)
|
|
||||||
img = img.convert("P",
|
img = img.convert("P",
|
||||||
dither=Image.NONE,
|
dither=Image.NONE,
|
||||||
palette=Image.ADAPTIVE,
|
palette=Image.ADAPTIVE,
|
||||||
colors=palette_max_size)
|
colors=palette_max_size)
|
||||||
|
|
||||||
# Format for the first palette is a list of N triplets where N is the
|
# The palette format is a list of N triplets where N includes both the
|
||||||
# number of used opaque colors; obviously N <= palette_max_size
|
# opaque colors we just generated and an alpha color. Sometimes colors
|
||||||
# Note: sometimes colors are not numbered 0..N-1, so we take the max
|
# after img.convert() are not numbered 0..N-1, so take the max.
|
||||||
# value rather than len(img.getcolors()); we don't try to remap indices
|
|
||||||
pixels = img.load()
|
pixels = img.load()
|
||||||
N = 1 + max(pixels[x,y]
|
N = 1 + max(pixels[x,y]
|
||||||
for y in range(img.height)
|
for y in range(img.height) for x in range(img.width))
|
||||||
for x in range(img.width))
|
|
||||||
palette1 = img.getpalette()[:3*N]
|
palette = img.getpalette()[:3*N]
|
||||||
palette1 = list(zip(palette1[::3], palette1[1::3], palette1[2::3]))
|
palette = list(zip(palette[::3], palette[1::3], palette[2::3]))
|
||||||
|
|
||||||
|
# For formats with transparency, make the transparent color consistent
|
||||||
|
if format.has_alpha:
|
||||||
|
N += 1
|
||||||
|
palette = [(255, 0, 255)] + palette
|
||||||
|
|
||||||
|
# Also keep track of how to remap indices from the values generated by
|
||||||
|
# img.convert() into the palette, which is shifted by 1 due to alpha
|
||||||
|
# and also starts at format.palette_base. Note: format.palette_base
|
||||||
|
# already starts 1 value later for formats with alpha.
|
||||||
|
palette_map = [(format.palette_base + i) % format.color_count
|
||||||
|
for i in range(N)]
|
||||||
else:
|
else:
|
||||||
pixels = img.load()
|
px = img.load()
|
||||||
|
|
||||||
#---
|
#---
|
||||||
# Alpha encoding
|
# Encode data into a bytearray
|
||||||
# Find a value to encode transparency and map it into palettes.
|
|
||||||
#---
|
#---
|
||||||
|
|
||||||
# RGB565A with fixed alpha value (fi. alpha=0x0001 in libimg)
|
def rgb24to16(rgb):
|
||||||
if color_count == 0 and force_alpha is not None:
|
r = (rgb[0] & 0xff) >> 3
|
||||||
alpha, replacement = force_alpha
|
g = (rgb[1] & 0xff) >> 2
|
||||||
|
b = (rgb[2] & 0xff) >> 3
|
||||||
|
return (r << 11) | (g << 5) | b
|
||||||
|
|
||||||
# For palettes, anything works; use the next value
|
if format.depth == IMAGE_RGB16:
|
||||||
elif color_count > 0 and (has_alpha or force_alpha == True):
|
# Preserve alignment between rows by padding to 4 bytes
|
||||||
alpha = N
|
|
||||||
|
|
||||||
# Find an unused RGB565 value and keep the encoding to 16-bit
|
|
||||||
elif has_alpha:
|
|
||||||
colormap = { rgb24to16(pixels[x, y])
|
|
||||||
for x in range(img.width) for y in range(img.height)
|
|
||||||
if alpha_pixels[x, y] > 0 }
|
|
||||||
|
|
||||||
available = set(range(65536)) - colormap
|
|
||||||
|
|
||||||
if not available:
|
|
||||||
raise FxconvError("image uses all 65536 colors and alpha")
|
|
||||||
alpha = available.pop()
|
|
||||||
|
|
||||||
# No transparency in the image
|
|
||||||
else:
|
|
||||||
alpha = None
|
|
||||||
|
|
||||||
# Function to encode with alpha support in RGB565
|
|
||||||
def alpha_encoding(color, a):
|
|
||||||
if a > 0: # pixel is not transparent
|
|
||||||
return color if color != alpha else replacement
|
|
||||||
else:
|
|
||||||
return alpha
|
|
||||||
|
|
||||||
# In palette formats, rearrange the palette to account for modulo numbering
|
|
||||||
# and trim the palette if needed
|
|
||||||
|
|
||||||
if color_count > 0:
|
|
||||||
total = len(palette1) + int(has_alpha or force_alpha == True)
|
|
||||||
# The palette_map list associates to indices into palette1, the pixel
|
|
||||||
# value in the data array (which starts at palette_base)
|
|
||||||
palette_map = [(palette_base + i) % color_count for i in range(total)]
|
|
||||||
|
|
||||||
#---
|
|
||||||
# Image encoding
|
|
||||||
# Create byte arrays with pixel data and palette data
|
|
||||||
#---
|
|
||||||
|
|
||||||
stride = 0
|
|
||||||
|
|
||||||
if not color_count:
|
|
||||||
# In RGB16, preserve alignment between rows
|
|
||||||
stride = (img.width + 1) // 2 * 4
|
stride = (img.width + 1) // 2 * 4
|
||||||
size = stride * img.height * 2
|
size = stride * img.height * 2
|
||||||
elif color_count == 256:
|
elif format.depth == IMAGE_P8:
|
||||||
# No constraint in P8
|
|
||||||
size = img.width * img.height
|
size = img.width * img.height
|
||||||
stride = img.width
|
stride = img.width
|
||||||
elif color_count == 16:
|
elif format.depth == IMAGE_P4:
|
||||||
# In P4, pad whole bytes
|
# Pad whole bytes
|
||||||
stride = (img.width + 1) // 2
|
stride = (img.width + 1) // 2
|
||||||
size = stride * img.height
|
size = stride * img.height
|
||||||
|
|
||||||
# Result of encoding
|
# Encode the pixel data
|
||||||
encoded = bytearray(size)
|
data = bytearray(size)
|
||||||
# Offset into the array
|
|
||||||
offset = 0
|
|
||||||
|
|
||||||
for y in range(img.height):
|
for y in range(img.height):
|
||||||
for x in range(img.width):
|
for x in range(img.width):
|
||||||
a = alpha_pixels[x, y] if has_alpha else 255
|
a = alpha_pixels[x,y]
|
||||||
|
|
||||||
if not color_count:
|
if format.depth == IMAGE_RGB16:
|
||||||
c = alpha_encoding(rgb24to16(pixels[x, y]), a)
|
# If c lands on the alpha value, flip its lowest bit
|
||||||
|
c = rgb24to16(pixels[x, y])
|
||||||
|
c = format.alpha if (a == 0) else c ^ (c == format.alpha)
|
||||||
offset = (stride * y) + x * 2
|
offset = (stride * y) + x * 2
|
||||||
encoded[offset:offset+2] = u16(c)
|
data[offset:offset+2] = u16(c)
|
||||||
|
|
||||||
elif color_count == 16:
|
elif format.depth == IMAGE_P8:
|
||||||
c = palette_map[pixels[x, y] if a > 0 else alpha]
|
c = palette_map[pixels[x, y]] if a > 0 else format.alpha
|
||||||
|
offset = (stride * y) + x
|
||||||
|
data[offset] = c
|
||||||
|
|
||||||
# Select either the 4 MSb or 4 LSb of the current byte
|
elif format.depth == IMAGE_P4:
|
||||||
|
c = palette_map[pixels[x, y]] if a > 0 else format.alpha
|
||||||
|
offset = (stride * y) + (x // 2)
|
||||||
if x % 2 == 0:
|
if x % 2 == 0:
|
||||||
encoded[offset] |= (c << 4)
|
data[offset] |= (c << 4)
|
||||||
else:
|
else:
|
||||||
encoded[offset] |= c
|
data[offset] |= c
|
||||||
|
|
||||||
offset += (x % 2 == 1) or (x == img.width - 1)
|
# Encode the palette
|
||||||
|
|
||||||
elif color_count == 256:
|
if format.is_indexed:
|
||||||
c = palette_map[pixels[x, y] if a > 0 else alpha]
|
N = N if format.trim_palette else format.color_count
|
||||||
encoded[offset] = c
|
encoded_palette = bytearray(2 * N)
|
||||||
offset += 1
|
for i, rgb24 in enumerate(palette):
|
||||||
|
encoded_palette[2*i:2*i+2] = u16(rgb24to16(rgb24))
|
||||||
# Encode the palette as R5G6B5
|
return data, stride, encoded_palette, N
|
||||||
|
|
||||||
if color_count > 0:
|
|
||||||
if trim_palette:
|
|
||||||
encoded_palette = bytearray(2 * len(palette_map))
|
|
||||||
else:
|
|
||||||
encoded_palette = bytearray(2 * color_count)
|
|
||||||
|
|
||||||
for i in range(len(palette1)):
|
|
||||||
encoded_palette[2*i:2*i+2] = u16(rgb24to16(palette1[i]))
|
|
||||||
|
|
||||||
#---
|
|
||||||
# Outro
|
|
||||||
#---
|
|
||||||
|
|
||||||
if color_count > 0:
|
|
||||||
if alpha is not None:
|
|
||||||
alpha = palette_map[alpha]
|
|
||||||
return encoded, stride, encoded_palette, alpha
|
|
||||||
else:
|
else:
|
||||||
return encoded, stride, alpha
|
return data, stride, None, -1
|
||||||
|
|
||||||
def convert(input, params, target, output=None, model=None, custom=None):
|
def convert(input, params, target, output=None, model=None, custom=None):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in a new issue