mirror of
https://git.lynn.is/Gwen/python-layout.git
synced 2024-01-13 01:31:55 +01:00
267 lines
12 KiB
Python
267 lines
12 KiB
Python
from PIL import ImageDraw, Image as PilImage
|
|
|
|
from . import Layout, ImageMode, ImageAnchor
|
|
from .internal.helpers import max_with_none, min_with_none
|
|
|
|
|
|
class Image(Layout):
|
|
def __init__(
|
|
self,
|
|
|
|
image=None,
|
|
mode=ImageMode.ORIGINAL,
|
|
scale=1,
|
|
position=ImageAnchor.TOP_LEFT,
|
|
offset_x=0,
|
|
offset_y=0,
|
|
|
|
**kwargs
|
|
):
|
|
super().__init__(**kwargs)
|
|
self.image = image
|
|
self.mode = mode
|
|
self.scale = scale
|
|
self.position = position
|
|
self.offset_x = offset_x
|
|
self.offset_y = offset_y
|
|
|
|
def get_scale(self):
|
|
if isinstance(self.scale, (tuple, list)):
|
|
if len(self.scale) == 0:
|
|
return 1, 1
|
|
elif len(self.scale) == 1:
|
|
return self.scale[0], self.scale[0]
|
|
else:
|
|
return self.scale[0], self.scale[1]
|
|
elif isinstance(self.scale, (int, float)):
|
|
return self.scale, self.scale
|
|
else:
|
|
return 1, 1
|
|
|
|
def get_image_width(self):
|
|
if self.image is None:
|
|
return 0
|
|
return self.image.size[0]
|
|
|
|
def get_image_height(self):
|
|
if self.image is None:
|
|
return 0
|
|
return self.image.size[1]
|
|
|
|
def get_min_inner_width(self, max_height=None):
|
|
if self.mode in (ImageMode.ORIGINAL, ImageMode.REPEAT_Y, ImageMode.STRETCH_Y):
|
|
width = self.get_image_width()
|
|
else:
|
|
width = 0
|
|
return max_with_none(width, self.min_width)
|
|
|
|
def get_min_inner_height(self, max_width=None):
|
|
if self.mode in (ImageMode.ORIGINAL, ImageMode.REPEAT_X, ImageMode.STRETCH_X):
|
|
height = self.get_image_height()
|
|
else:
|
|
height = 0
|
|
return max_with_none(height, self.min_height)
|
|
|
|
def get_ideal_inner_dimensions(self, min_width=None, min_height=None, available_width=None, available_height=None):
|
|
width, height = 0, 0
|
|
if self.mode in (ImageMode.ORIGINAL, ImageMode.REPEAT_Y, ImageMode.STRETCH_Y):
|
|
width = self.get_image_width()
|
|
if self.mode in (ImageMode.ORIGINAL, ImageMode.REPEAT_X, ImageMode.STRETCH_X):
|
|
height = self.get_image_height()
|
|
if self.width is not None:
|
|
width = self.width
|
|
if self.height is not None:
|
|
height = self.height
|
|
width = min_with_none(width, available_width, self.max_width)
|
|
height = min_with_none(height, available_height, self.max_height)
|
|
width = max_with_none(width, min_width, self.min_width)
|
|
height = max_with_none(height, min_height, self.min_height)
|
|
return width, height
|
|
|
|
def render_content(self, rect):
|
|
image = self.make_canvas()
|
|
if self.image is None:
|
|
return image
|
|
|
|
x1, y1, x2, y2 = rect
|
|
width = x2 - x1 + 1
|
|
height = y2 - y1 + 1
|
|
|
|
if width == 0 or height == 0:
|
|
return image
|
|
|
|
image_width = int(self.image.size[0] * self.get_scale()[0])
|
|
image_height = int(self.image.size[1] * self.get_scale()[1])
|
|
|
|
offset_x = self.offset_x
|
|
offset_y = self.offset_y
|
|
|
|
if self.mode == ImageMode.ORIGINAL:
|
|
if image_width != self.image.size[0] or image_height != self.image.size[1]:
|
|
img = self.image.resize((image_width, image_height), reducing_gap=3.0)
|
|
else:
|
|
img = self.image
|
|
elif self.mode == ImageMode.STRETCH:
|
|
img = self.image.resize((width, height), reducing_gap=3.0)
|
|
elif self.mode == ImageMode.STRETCH_X:
|
|
img = self.image.resize((width, image_height), reducing_gap=3.0)
|
|
elif self.mode == ImageMode.STRETCH_Y:
|
|
img = self.image.resize((image_width, height), reducing_gap=3.0)
|
|
elif self.mode == ImageMode.CONTAIN:
|
|
ratio_width = width / image_width
|
|
ratio_height = height / image_height
|
|
if ratio_width < ratio_height:
|
|
w = width
|
|
h = int(image_height * width / image_width)
|
|
elif ratio_height < ratio_width:
|
|
w = int(image_width * height / image_height)
|
|
h = height
|
|
else:
|
|
w = width
|
|
h = height
|
|
img = self.image.resize((w, h), reducing_gap=3.0)
|
|
elif self.mode == ImageMode.COVER:
|
|
ratio_width = width / image_width
|
|
ratio_height = height / image_height
|
|
if ratio_width > ratio_height:
|
|
w = width
|
|
h = int(image_height * width / image_width)
|
|
elif ratio_height > ratio_width:
|
|
w = int(image_width * height / image_height)
|
|
h = height
|
|
else:
|
|
w = width
|
|
h = height
|
|
img = self.image.resize((w, h), reducing_gap=3.0)
|
|
|
|
if self.mode == ImageMode.REPEAT_X_FILL:
|
|
repeat_width = int(image_width * height / image_height)
|
|
repeat_height = height
|
|
elif self.mode == ImageMode.REPEAT_Y_FILL:
|
|
repeat_width = width
|
|
repeat_height = int(image_height * width / image_width)
|
|
else:
|
|
repeat_width = image_width
|
|
repeat_height = image_height
|
|
|
|
if self.mode in (ImageMode.REPEAT, ImageMode.REPEAT_X, ImageMode.REPEAT_X_FILL, ImageMode.REPEAT_X_STRETCH):
|
|
if self.position in (ImageAnchor.TOP_LEFT, ImageAnchor.MIDDLE_LEFT, ImageAnchor.BOTTOM_LEFT):
|
|
repeat_offset_x = offset_x
|
|
elif self.position in (ImageAnchor.TOP_CENTER, ImageAnchor.MIDDLE_CENTER, ImageAnchor.BOTTOM_CENTER):
|
|
repeat_offset_x = (width // 2) - repeat_width // 2 + offset_x
|
|
elif self.position in (ImageAnchor.TOP_RIGHT, ImageAnchor.MIDDLE_RIGHT, ImageAnchor.BOTTOM_RIGHT):
|
|
repeat_offset_x = width - repeat_width + offset_x
|
|
else:
|
|
raise Exception('invalid image position')
|
|
if self.mode in (ImageMode.REPEAT, ImageMode.REPEAT_Y, ImageMode.REPEAT_Y_FILL, ImageMode.REPEAT_Y_STRETCH):
|
|
if self.position in (ImageAnchor.TOP_LEFT, ImageAnchor.TOP_CENTER, ImageAnchor.TOP_RIGHT):
|
|
repeat_offset_y = offset_y
|
|
elif self.position in (ImageAnchor.MIDDLE_LEFT, ImageAnchor.MIDDLE_CENTER, ImageAnchor.MIDDLE_RIGHT):
|
|
repeat_offset_y = (height // 2) - repeat_height // 2 + offset_y
|
|
elif self.position in (ImageAnchor.BOTTOM_LEFT, ImageAnchor.BOTTOM_CENTER, ImageAnchor.BOTTOM_RIGHT):
|
|
repeat_offset_y = height - repeat_height + offset_y
|
|
else:
|
|
raise Exception('invalid image position')
|
|
|
|
if self.mode == ImageMode.REPEAT:
|
|
if repeat_width != self.image.size[0] or repeat_height != self.image.size[1]:
|
|
img_part = self.image.resize((repeat_width, repeat_height), reducing_gap=3.0)
|
|
else:
|
|
img_part = self.image
|
|
img = PilImage.new('RGBA', (width, height), (0,0,0,0))
|
|
num_x = width // repeat_width + 1
|
|
num_y = height // repeat_height + 1
|
|
ox = repeat_offset_x % (repeat_width if repeat_offset_x >= 0 else -repeat_width)
|
|
oy = repeat_offset_y % (repeat_height if repeat_offset_y >= 0 else -repeat_height)
|
|
for i in range(-1, num_x):
|
|
for j in range(-1, num_y):
|
|
img.paste(img_part, (i * repeat_width + ox, j * repeat_height + oy))
|
|
offset_x = 0
|
|
offset_y = 0
|
|
elif self.mode == ImageMode.REPEAT_X:
|
|
if repeat_width != self.image.size[0] or repeat_height != self.image.size[1]:
|
|
img_part = self.image.resize((repeat_width, repeat_height), reducing_gap=3.0)
|
|
else:
|
|
img_part = self.image
|
|
img = PilImage.new('RGBA', (width, repeat_height), (0,0,0,0))
|
|
num_x = width // repeat_width + 1
|
|
ox = repeat_offset_x % (repeat_width if repeat_offset_x >= 0 else -repeat_width)
|
|
for i in range(-1, num_x):
|
|
img.paste(img_part, (i * repeat_width + ox, 0))
|
|
offset_x = 0
|
|
elif self.mode == ImageMode.REPEAT_Y:
|
|
if repeat_width != self.image.size[0] or repeat_height != self.image.size[1]:
|
|
img_part = self.image.resize((repeat_width, repeat_height), reducing_gap=3.0)
|
|
else:
|
|
img_part = self.image
|
|
img = PilImage.new('RGBA', (repeat_width, height), (0,0,0,0))
|
|
num_y = height // repeat_height + 1
|
|
oy = repeat_offset_y % (repeat_height if repeat_offset_y >= 0 else -repeat_height)
|
|
for i in range(-1, num_y):
|
|
img.paste(img_part, (0, i * repeat_height + oy))
|
|
offset_y = 0
|
|
elif self.mode == ImageMode.REPEAT_X_STRETCH:
|
|
if repeat_width != self.image.size[0] or height != self.image.size[1]:
|
|
img_part = self.image.resize((repeat_width, height), reducing_gap=3.0)
|
|
else:
|
|
img_part = self.image
|
|
img = PilImage.new('RGBA', (width, height), (0,0,0,0))
|
|
num_x = width // repeat_width + 1
|
|
ox = repeat_offset_x % (repeat_width if repeat_offset_x >= 0 else -repeat_width)
|
|
for i in range(-1, num_x):
|
|
img.paste(img_part, (i * repeat_width + ox, 0))
|
|
offset_x = 0
|
|
elif self.mode == ImageMode.REPEAT_Y_STRETCH:
|
|
if width != self.image.size[0] or repeat_height != self.image.size[1]:
|
|
img_part = self.image.resize((width, repeat_height), reducing_gap=3.0)
|
|
else:
|
|
img_part = self.image
|
|
img = PilImage.new('RGBA', (width, height), (0,0,0,0))
|
|
num_y = height // repeat_height + 1
|
|
oy = repeat_offset_y % (repeat_height if repeat_offset_y >= 0 else -repeat_height)
|
|
for i in range(-1, num_y):
|
|
img.paste(img_part, (0, i * repeat_height + oy))
|
|
offset_y = 0
|
|
elif self.mode == ImageMode.REPEAT_X_FILL:
|
|
if repeat_width != self.image.size[0] or repeat_height != self.image.size[1]:
|
|
img_part = self.image.resize((repeat_width, repeat_height), reducing_gap=3.0)
|
|
else:
|
|
img_part = self.image
|
|
img = PilImage.new('RGBA', (width, height), (0,0,0,0))
|
|
num_x = width // repeat_width + 1
|
|
ox = repeat_offset_x % (repeat_width if repeat_offset_x >= 0 else -w)
|
|
for i in range(-1, num_x):
|
|
img.paste(img_part, (i * repeat_width + ox, 0))
|
|
offset_x = 0
|
|
elif self.mode == ImageMode.REPEAT_Y_FILL:
|
|
if repeat_width != self.image.size[0] or repeat_height != self.image.size[1]:
|
|
img_part = self.image.resize((repeat_width, repeat_height), reducing_gap=3.0)
|
|
else:
|
|
img_part = self.image
|
|
img = PilImage.new('RGBA', (width, height), (0,0,0,0))
|
|
num_y = height // repeat_height + 1
|
|
oy = repeat_offset_y % (repeat_height if repeat_offset_y >= 0 else -repeat_height)
|
|
for i in range(-1, num_y):
|
|
img.paste(img_part, (0, i * repeat_height + oy))
|
|
offset_y = 0
|
|
|
|
if self.position in (ImageAnchor.TOP_LEFT, ImageAnchor.MIDDLE_LEFT, ImageAnchor.BOTTOM_LEFT):
|
|
x = x1 + offset_x
|
|
elif self.position in (ImageAnchor.TOP_CENTER, ImageAnchor.MIDDLE_CENTER, ImageAnchor.BOTTOM_CENTER):
|
|
x = x1 + (width // 2) - img.size[0] // 2 + offset_x
|
|
elif self.position in (ImageAnchor.TOP_RIGHT, ImageAnchor.MIDDLE_RIGHT, ImageAnchor.BOTTOM_RIGHT):
|
|
x = x1 + width - img.size[0] + offset_x
|
|
else:
|
|
raise Exception('invalid image position')
|
|
if self.position in (ImageAnchor.TOP_LEFT, ImageAnchor.TOP_CENTER, ImageAnchor.TOP_RIGHT):
|
|
y = y1 + offset_y
|
|
elif self.position in (ImageAnchor.MIDDLE_LEFT, ImageAnchor.MIDDLE_CENTER, ImageAnchor.MIDDLE_RIGHT):
|
|
y = y1 + (height // 2) - img.size[1] // 2 + offset_y
|
|
elif self.position in (ImageAnchor.BOTTOM_LEFT, ImageAnchor.BOTTOM_CENTER, ImageAnchor.BOTTOM_RIGHT):
|
|
y = y1 + height - img.size[1] + offset_y
|
|
else:
|
|
raise Exception('invalid image position')
|
|
|
|
image.paste(img, (x, y))
|
|
return image
|