automeme/pillow_fight.py

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()