278 lines
12 KiB
Python
278 lines
12 KiB
Python
import math
|
|
from typing import Union
|
|
from PIL import Image
|
|
from PIL import ImageDraw
|
|
from PIL import ImageFont
|
|
from PIL import ImageOps
|
|
import requests
|
|
import io
|
|
from image_utils import ImageText
|
|
from os.path import exists
|
|
|
|
def create_soy_vs_chad_meme(emoji1, emoji2, caption1, caption2):
|
|
LEFT_MARGIN_COLUMN = 20
|
|
CONTENT_COLUMN = 300
|
|
MIDDLE_MARGIN_COLUMN = 80
|
|
RIGHT_MARGIN_COLUMN = LEFT_MARGIN_COLUMN
|
|
TOP_MARGIN_ROW = 80
|
|
IMAGE_ROW = 300
|
|
MIDDLE_MARGIN_ROW = 20
|
|
TEXT_ROW = 100
|
|
BOTTOM_MARGIN_ROW = 200
|
|
|
|
total_image_size_x = 2*CONTENT_COLUMN + LEFT_MARGIN_COLUMN + RIGHT_MARGIN_COLUMN + MIDDLE_MARGIN_COLUMN
|
|
total_image_size_y = TOP_MARGIN_ROW + IMAGE_ROW + MIDDLE_MARGIN_ROW + TEXT_ROW + BOTTOM_MARGIN_ROW
|
|
|
|
left_image = get_emoji_from_rdrama(emoji1)
|
|
right_image = get_emoji_from_rdrama(emoji2)
|
|
|
|
#Resize images
|
|
left_image = ImageOps.contain(left_image, (CONTENT_COLUMN, IMAGE_ROW))
|
|
right_image = ImageOps.contain(right_image, (CONTENT_COLUMN, IMAGE_ROW))
|
|
|
|
right_image = ImageOps.mirror(right_image)
|
|
|
|
#Base image
|
|
base = Image.new(mode="RGB", size=(total_image_size_x,total_image_size_y), color=(255,255,255))
|
|
|
|
#Add images
|
|
center_and_paste(base, left_image, (LEFT_MARGIN_COLUMN,TOP_MARGIN_ROW), (CONTENT_COLUMN, IMAGE_ROW))
|
|
center_and_paste(base, right_image, (LEFT_MARGIN_COLUMN+CONTENT_COLUMN+MIDDLE_MARGIN_COLUMN,TOP_MARGIN_ROW), (CONTENT_COLUMN, IMAGE_ROW))
|
|
|
|
#Text regions
|
|
add_text(base, caption1, (CONTENT_COLUMN, TEXT_ROW), (LEFT_MARGIN_COLUMN, TOP_MARGIN_ROW+IMAGE_ROW+MIDDLE_MARGIN_ROW))
|
|
add_text(base, caption2, (CONTENT_COLUMN, TEXT_ROW), (LEFT_MARGIN_COLUMN+CONTENT_COLUMN+MIDDLE_MARGIN_COLUMN, TOP_MARGIN_ROW+IMAGE_ROW+MIDDLE_MARGIN_ROW))
|
|
return add_watermark(base)
|
|
|
|
class WebcomicPanel():
|
|
PANEL_SIZE = 400
|
|
FONT = "Little Story.ttf"
|
|
|
|
def __init__(self):
|
|
pass
|
|
|
|
def create_image(self) -> Image:
|
|
return Image.new(mode="RGB", size=(self.PANEL_SIZE,self.PANEL_SIZE), color=(255,255,255))
|
|
|
|
class OneCharacterWebcomicPanel(WebcomicPanel):
|
|
def __init__(self, emoji, caption):
|
|
self.emoji = emoji
|
|
self.caption = caption
|
|
|
|
def create_image(self) -> Image:
|
|
base = super().create_image()
|
|
panel_size_x, panel_size_y = base.size
|
|
|
|
# We put marsey in the bottom left quadrant
|
|
emoji_region_x_size = int(panel_size_x/2)
|
|
emoji_region_y_size = int(panel_size_y/2)
|
|
emoji_placement_position_x = 0
|
|
emoji_placement_position_y = int(panel_size_y/2)
|
|
emoji = get_emoji_from_rdrama(self.emoji)
|
|
center_and_paste(base, emoji, (emoji_placement_position_x, emoji_placement_position_y), (emoji_region_x_size, emoji_region_y_size))
|
|
|
|
# We put the text in the top half of the panel.
|
|
text_region_x_size = int(panel_size_x)
|
|
text_region_y_size = int(panel_size_y/2)
|
|
add_text(base, self.caption, (text_region_x_size, text_region_y_size), (0,0), font=super().FONT)
|
|
|
|
return add_border_to_image(base)
|
|
|
|
class TwoCharacterWebcomicPanel(WebcomicPanel):
|
|
def __init__(self, left_emoji, left_caption, right_emoji, right_caption):
|
|
self.left_emoji = left_emoji
|
|
self.left_caption = left_caption
|
|
self.right_emoji = right_emoji
|
|
self.right_caption = right_caption
|
|
|
|
def create_image(self) -> Image:
|
|
base = super().create_image()
|
|
panel_size_x, panel_size_y = base.size
|
|
|
|
# We put the left marsey in the bottom left quadrant
|
|
left_emoji_region_x_size = int(panel_size_x/2)
|
|
left_emoji_region_y_size = int(panel_size_y/2)
|
|
left_emoji_placement_position_x = 0
|
|
left_emoji_placement_position_y = int(panel_size_y/2)
|
|
left_emoji = get_emoji_from_rdrama(self.left_emoji)
|
|
center_and_paste(base, left_emoji, (left_emoji_placement_position_x, left_emoji_placement_position_y), (left_emoji_region_x_size, left_emoji_region_y_size))
|
|
|
|
# We put the right marsey in the bottom right quadrant
|
|
right_emoji_region_x_size = int(panel_size_x/2)
|
|
right_emoji_region_y_size = int(panel_size_y/2)
|
|
right_emoji_placement_position_x = int(panel_size_x/2)
|
|
right_emoji_placement_position_y = int(panel_size_y/2)
|
|
right_emoji = get_emoji_from_rdrama(self.right_emoji)
|
|
center_and_paste(base, right_emoji, (right_emoji_placement_position_x, right_emoji_placement_position_y), (right_emoji_region_x_size, right_emoji_region_y_size))
|
|
|
|
#Each text caption will get 5/8 of the width, and 1/4 of the height of the panel.
|
|
#The left caption will be all the way to the left. The right caption will be 3/8 to the right.
|
|
|
|
CAPTION_UNITS = 5
|
|
CAPTION_DIVISOR = 8
|
|
|
|
# We put the text in the top half of the panel.
|
|
left_text_region_x_size = int(CAPTION_UNITS*(panel_size_x/CAPTION_DIVISOR))
|
|
left_text_region_y_size = int(panel_size_y/4)
|
|
left_text_region_x_position = 0
|
|
left_text_region_y_position = 0
|
|
add_text_box(base, self.left_caption, (left_text_region_x_size, left_text_region_y_size), (left_text_region_x_position,left_text_region_y_position), font=super().FONT, align="bl")
|
|
|
|
# We put the text in the top half of the panel.
|
|
right_text_region_x_size = int(CAPTION_UNITS*(panel_size_x/CAPTION_DIVISOR))
|
|
right_text_region_y_size = int(panel_size_y/4)
|
|
right_text_region_x_position = int((CAPTION_DIVISOR-CAPTION_UNITS)*(panel_size_x/8))
|
|
right_text_region_y_position = int(panel_size_y/4)
|
|
add_text_box(base, self.right_caption, (right_text_region_x_size, right_text_region_y_size), (right_text_region_x_position,right_text_region_y_position), font=super().FONT, align="br")
|
|
|
|
return add_border_to_image(base)
|
|
|
|
class TitleCardWebcomicPanel(WebcomicPanel):
|
|
def __init__(self, caption):
|
|
self.caption = caption
|
|
|
|
def create_image(self) -> Image:
|
|
base = super().create_image()
|
|
|
|
add_text(base, self.caption, base.size, (0,0), font=super().FONT)
|
|
|
|
return add_border_to_image(base)
|
|
|
|
def create_webcomic(layout : 'list[WebcomicPanel]'):
|
|
assumed_panel_x_size, assumed_panel_y_size = layout[0].create_image().size
|
|
total_image_x_size = assumed_panel_x_size * 2
|
|
total_image_y_size = assumed_panel_x_size * math.ceil(len(layout)/2)
|
|
|
|
image = Image.new(mode="RGB", size=(total_image_x_size, total_image_y_size), color=(255,255,255))
|
|
for i in range(len(layout)):
|
|
panel = layout[i]
|
|
x = i%2
|
|
y = math.floor(i/2)
|
|
image.paste(panel.create_image(), (x*assumed_panel_x_size, y*assumed_panel_y_size))
|
|
return add_watermark(image)
|
|
|
|
def add_text_box(base : Image, caption : str, region_size : tuple[int, int], coordinates : tuple[int, int], font : str= "arial.ttf", init_font_size = 45, align :str = ""):
|
|
if caption == "":
|
|
return
|
|
if init_font_size == 0:
|
|
print("Retard moment")
|
|
return
|
|
region_x_size, region_y_size = region_size
|
|
line_image = ImageText((region_x_size, region_y_size))
|
|
actual_text_box_size = line_image.write_text_box((0,0), caption, region_x_size, font_size=init_font_size, font_filename=font)
|
|
actual_text_box_x_size, actual_text_box_y_size, input_text_block_x_size = actual_text_box_size
|
|
if actual_text_box_y_size <= region_y_size:
|
|
actual_paste_x_coordinates, actual_paste_y_coordinates = coordinates
|
|
if align != "":
|
|
# Horizontal Align
|
|
if "t" in align:
|
|
#This is the default
|
|
pass
|
|
elif "b" in align:
|
|
y_difference = region_y_size - actual_text_box_y_size
|
|
actual_paste_y_coordinates+=y_difference
|
|
|
|
if "l" in align:
|
|
#This is the default
|
|
pass
|
|
elif "r" in align:
|
|
x_difference = region_x_size - actual_text_box_x_size
|
|
actual_paste_x_coordinates+=x_difference
|
|
|
|
base.paste(line_image.image, (actual_paste_x_coordinates, actual_paste_y_coordinates), line_image.image)
|
|
else:
|
|
add_text_box(base, caption, region_size, coordinates, font=font, init_font_size=init_font_size-1)
|
|
|
|
def add_text(base : Image, caption : str, region_size : tuple[int, int], coordinates : tuple[int, int], font : str= "arial.ttf"):
|
|
if caption == "":
|
|
return
|
|
region_x_size, region_y_size = region_size
|
|
line_image = ImageText((region_x_size, region_y_size))
|
|
line_image.fill_text_box((0,0), caption, region_x_size, region_y_size, font_filename=font)
|
|
base.paste(line_image.image, coordinates, line_image.image)
|
|
|
|
def add_watermark(image : Image):
|
|
WATERMARK_HEIGHT = 30
|
|
image_size_x, image_size_y = image.size
|
|
base = Image.new(mode="RGB", size=(image_size_x, image_size_y + WATERMARK_HEIGHT), color=(255,255,255))
|
|
base.paste(image)
|
|
|
|
marsey = get_emoji_from_rdrama("marseyjamming")
|
|
marsey = ImageOps.contain(marsey, (WATERMARK_HEIGHT, WATERMARK_HEIGHT))
|
|
base.paste(marsey, (0, image_size_y), marsey)
|
|
|
|
text_line_size = int(WATERMARK_HEIGHT/2)
|
|
|
|
add_text(base, "A meme by HeyMoon and Foo", (image_size_x, text_line_size), (WATERMARK_HEIGHT, image_size_y))
|
|
add_text(base, "For instructions on how to legally build a pipe bomb, go to rdrama.net", (image_size_x, text_line_size), (WATERMARK_HEIGHT, image_size_y+text_line_size))
|
|
|
|
return base
|
|
|
|
def center_and_paste(base : Image, to_paste : Image, coordinates: tuple[int, int], box_size : tuple[int, int]):
|
|
to_paste = ImageOps.contain(to_paste, box_size)
|
|
|
|
image_size_x, image_size_y = to_paste.size
|
|
box_size_x, box_size_y = box_size
|
|
|
|
extra_space_x = box_size_x - image_size_x
|
|
extra_space_y = box_size_y - image_size_y
|
|
|
|
offset_x = int(extra_space_x/2)
|
|
offset_y = int(extra_space_y/2)
|
|
|
|
box_coordinate_x, box_coordinate_y = coordinates
|
|
|
|
x, y = offset_x + box_coordinate_x, offset_y + box_coordinate_y
|
|
|
|
base.paste(to_paste, (x, y), to_paste)
|
|
|
|
def add_border_to_image(image : Image, thickness : int = 5):
|
|
# image_draw = ImageDraw.Draw(image)
|
|
# image_x_size, image_y_size = image.size
|
|
# inner_rectangle_x_size, inner_rectangle_y_size = image_x_size - 2*thickness, image_y_size - 2*thickness
|
|
# image_draw.rectangle([(0,0),(image_x_size, image_y_size)], fill="black")
|
|
# image_draw.rectangle([(thickness, thickness),(image_x_size-thickness, image_y_size-thickness)], fill="white")
|
|
inner_image_x_size, inner_image_y_size = image.size
|
|
outer_image_x_size, outer_image_y_size = inner_image_x_size + 2*thickness, inner_image_y_size + 2*thickness
|
|
outside_image = Image.new(mode="RGB", size=(outer_image_x_size,outer_image_y_size), color=(0,0,0))
|
|
|
|
outside_image.paste(image, (thickness, thickness))
|
|
return outside_image
|
|
|
|
def wrap_text_for_font(text, region_size, font_size) -> Image:
|
|
image = Image.new(mode="RGB", size=region_size, color=(255,255,255))
|
|
imageDraw = ImageDraw.ImageDraw()
|
|
imageDraw.multiline_text()
|
|
|
|
def get_emoji_from_rdrama(emoji_name):
|
|
cleaned_emoji_name : str = emoji_name
|
|
should_flip = False
|
|
if '!' in emoji_name:
|
|
cleaned_emoji_name = cleaned_emoji_name.replace("!", "")
|
|
should_flip = True
|
|
|
|
if (exists(f"emoji_cache/{cleaned_emoji_name}.webp")):
|
|
image = Image.open(f"emoji_cache/{cleaned_emoji_name}.webp")
|
|
else:
|
|
image = get_image_file_from_url(f"https://www.rdrama.net/e/{cleaned_emoji_name}.webp")
|
|
image.save(f"emoji_cache/{cleaned_emoji_name}.webp")
|
|
if should_flip:
|
|
image = ImageOps.mirror(image)
|
|
return image
|
|
|
|
def get_image_file_from_url(url):
|
|
r = requests.get(url)
|
|
image_file = io.BytesIO(r.content)
|
|
im = Image.open(image_file)
|
|
return im
|
|
|
|
#create_soy_vs_chad_meme("bigsmilesoyjak", "!marseyshooting", "I have fun new toys and games for your children", "Die").show()
|
|
|
|
create_webcomic([
|
|
TwoCharacterWebcomicPanel("soyjak", "Black people deserve the rope", "marseyconfused", "Why?"),
|
|
TwoCharacterWebcomicPanel("seethejak", "Because they are degenerate!!!", "marseysmug", "Kinda like you?"),
|
|
OneCharacterWebcomicPanel("soycry", "No!! I am a pure aryan white boy and I am special Hitler told me so, so fuck off you nigger cunt. God I hate you so much, if I commit suicide its gonna be your fault, fuck you"),
|
|
TitleCardWebcomicPanel("One Hour Later..."),
|
|
TwoCharacterWebcomicPanel("marseytombstone", "", "!marseycry", "He had so much to live for"),
|
|
OneCharacterWebcomicPanel("marseylaugh", "Just Kidding!")
|
|
]).show() |