532 lines
23 KiB
Python
532 lines
23 KiB
Python
import math
|
|
from typing import Callable, Tuple, Union
|
|
from PIL import Image
|
|
from PIL import ImageDraw, ImageSequence
|
|
from PIL import ImageFont
|
|
from PIL import ImageOps
|
|
import requests
|
|
import io
|
|
from utils import get_real_filename
|
|
from image_utils import ImageText
|
|
from os.path import exists
|
|
from random import choice
|
|
|
|
HIGHLIGHT_MODE = False
|
|
CAPTION_FILENAME = "watermark_captions.txt"
|
|
|
|
class ColorScheme:
|
|
BLACK = 0
|
|
WHITE_WITH_BLACK_BORDER = 1
|
|
|
|
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 = 80
|
|
|
|
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 = left_image.fit((CONTENT_COLUMN, IMAGE_ROW))
|
|
right_image = right_image.fit((CONTENT_COLUMN, IMAGE_ROW))
|
|
|
|
right_image = right_image.flip()
|
|
|
|
#Base image
|
|
base = AnimatedImage.new((total_image_size_x,total_image_size_y))
|
|
|
|
#Add images
|
|
base = center_and_paste(base, left_image, (LEFT_MARGIN_COLUMN,TOP_MARGIN_ROW), (CONTENT_COLUMN, IMAGE_ROW))
|
|
base = center_and_paste(base, right_image, (LEFT_MARGIN_COLUMN+CONTENT_COLUMN+MIDDLE_MARGIN_COLUMN,TOP_MARGIN_ROW), (CONTENT_COLUMN, IMAGE_ROW))
|
|
|
|
#Text regions
|
|
base = add_text_box(base,
|
|
caption1,
|
|
(CONTENT_COLUMN, TEXT_ROW),
|
|
(LEFT_MARGIN_COLUMN, TOP_MARGIN_ROW+IMAGE_ROW+MIDDLE_MARGIN_ROW),
|
|
init_font_size=50,
|
|
align="cht",
|
|
font="impact.ttf",
|
|
color=ColorScheme.WHITE_WITH_BLACK_BORDER)
|
|
base = add_text_box(base,
|
|
caption2,
|
|
(CONTENT_COLUMN, TEXT_ROW),
|
|
(LEFT_MARGIN_COLUMN+CONTENT_COLUMN+MIDDLE_MARGIN_COLUMN, TOP_MARGIN_ROW+IMAGE_ROW+MIDDLE_MARGIN_ROW),
|
|
init_font_size=50,
|
|
align="cht",
|
|
font="impact.ttf",
|
|
color=ColorScheme.WHITE_WITH_BLACK_BORDER)
|
|
return base
|
|
|
|
def create_classic_meme(image: 'AnimatedImage', top_caption : str, bottom_caption : str) -> 'AnimatedImage':
|
|
image_x_size, image_y_size = image.size
|
|
UNIT = 5
|
|
caption_size = int(image_y_size/UNIT)
|
|
|
|
image = add_text_box(image, top_caption, (image_x_size, caption_size), (0,0), "impact.ttf", caption_size, align="cvch", color=ColorScheme.WHITE_WITH_BLACK_BORDER)
|
|
image = add_text_box(image, bottom_caption, (image_x_size, caption_size), (0,int((UNIT-1)*(image_y_size/UNIT))), "impact.ttf", caption_size, align="cvch", color=ColorScheme.WHITE_WITH_BLACK_BORDER)
|
|
|
|
return image
|
|
|
|
def create_classic_meme_from_url(url : str, top_caption : str, bottom_caption : str) -> 'AnimatedImage':
|
|
return create_classic_meme(get_image_file_from_url(url).stretch_maintain_aspect_ratio(500), top_caption, bottom_caption)
|
|
|
|
def create_classic_meme_from_emoji(emoji : str, top_caption : str, bottom_caption : str):
|
|
EMOJI_SIZE = 600
|
|
BORDER_SIZE = 100
|
|
|
|
marsey = get_emoji_from_rdrama(emoji)
|
|
marsey = marsey.fit((EMOJI_SIZE, EMOJI_SIZE))
|
|
|
|
base = AnimatedImage.new(size=(EMOJI_SIZE+2*BORDER_SIZE, EMOJI_SIZE+2*BORDER_SIZE))
|
|
base = base.paste(marsey, (BORDER_SIZE, BORDER_SIZE))
|
|
|
|
return create_classic_meme(base, top_caption, bottom_caption)
|
|
|
|
def create_modern_meme(image : 'AnimatedImage', caption : str) -> 'AnimatedImage':
|
|
CAPTION_SIZE = 150
|
|
image_x_size, image_y_size = image.size
|
|
base = AnimatedImage.new(size=(image_x_size,image_y_size+CAPTION_SIZE))
|
|
base = base.paste(image, (0, CAPTION_SIZE))
|
|
base = add_text_box(base, caption, (image_x_size, CAPTION_SIZE), (0,0), font="intreb.ttf",align="chcv")
|
|
return base
|
|
|
|
def create_modern_meme_from_url(url : str, caption : str):
|
|
return create_modern_meme(get_image_file_from_url(url).stretch_maintain_aspect_ratio(500), caption)
|
|
|
|
def create_modern_meme_from_emoji(emoji: str, caption: str):
|
|
EMOJI_SIZE = 600
|
|
BORDER_SIZE = 10
|
|
|
|
marsey = get_emoji_from_rdrama(emoji)
|
|
marsey = marsey.fit((EMOJI_SIZE, EMOJI_SIZE))
|
|
|
|
base = AnimatedImage.new((EMOJI_SIZE+2*BORDER_SIZE, EMOJI_SIZE+2*BORDER_SIZE))
|
|
base = base.paste(marsey, (BORDER_SIZE, BORDER_SIZE))
|
|
|
|
return create_modern_meme(base, caption)
|
|
|
|
class WebcomicPanel():
|
|
PANEL_SIZE = 400
|
|
FONT = "impact.ttf"
|
|
COLOR = ColorScheme.WHITE_WITH_BLACK_BORDER
|
|
|
|
def __init__(self):
|
|
pass
|
|
|
|
def create_image(self) -> Image:
|
|
return AnimatedImage.new((self.PANEL_SIZE,self.PANEL_SIZE))
|
|
|
|
def add_text_box(self, base : Image, caption : str, region_size : 'tuple[int, int]', coordinates : 'tuple[int, int]', align="") -> 'AnimatedImage':
|
|
return add_text_box(base, caption, region_size, coordinates, align=align, font=self.FONT, color=self.COLOR, init_font_size=int(self.PANEL_SIZE/10))
|
|
|
|
class OneCharacterWebcomicPanel(WebcomicPanel):
|
|
def __init__(self, emoji, caption, words_in_background):
|
|
self.emoji = emoji
|
|
self.caption = caption
|
|
self.words_in_background = words_in_background
|
|
|
|
def create_image(self) -> Image:
|
|
base = super().create_image()
|
|
panel_size_x, panel_size_y = base.size
|
|
|
|
# We put the text in the background of the panel.
|
|
text_region_x_size = int(panel_size_x)
|
|
text_region_y_size = int(panel_size_y if self.words_in_background else panel_size_y/2)
|
|
base = self.add_text_box(base, self.caption, (text_region_x_size, text_region_y_size), (0,0))
|
|
|
|
# We put marsey in the bottom left quadrant
|
|
emoji_region_x_size = int(panel_size_x)
|
|
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)
|
|
base = center_and_paste(base, emoji, (emoji_placement_position_x, emoji_placement_position_y), (emoji_region_x_size, emoji_region_y_size))
|
|
|
|
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)
|
|
base = 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)
|
|
base = 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
|
|
base = self.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), 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)
|
|
base = self.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), 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()
|
|
|
|
base = self.add_text_box(base, self.caption, base.size, (0,0), align="cvch")
|
|
|
|
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 = AnimatedImage.new((total_image_x_size, total_image_y_size))
|
|
for i in range(len(layout)):
|
|
panel = layout[i]
|
|
x = i%2
|
|
y = math.floor(i/2)
|
|
image = image.paste(panel.create_image(), (x*assumed_panel_x_size, y*assumed_panel_y_size))
|
|
return 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 = "", color = ColorScheme.BLACK):
|
|
font = get_real_filename(font)
|
|
|
|
if caption == "":
|
|
return base
|
|
if init_font_size == 0:
|
|
print("Retard moment")
|
|
return base
|
|
region_x_size, region_y_size = region_size
|
|
|
|
if color == ColorScheme.BLACK:
|
|
fill_color = (0,0,0)
|
|
stroke = None
|
|
stroke_size = 0
|
|
if color == ColorScheme.WHITE_WITH_BLACK_BORDER:
|
|
fill_color = (255,255,255)
|
|
stroke = (0,0,0)
|
|
stroke_size = 3
|
|
caption = caption.upper()
|
|
line_image = ImageText((region_x_size+stroke_size, region_y_size))
|
|
place = "left"
|
|
if "ch" in align:
|
|
place = "center"
|
|
|
|
actual_text_box_size = line_image.write_text_box((stroke_size,0), caption, region_x_size, font_size=init_font_size, font_filename=font, color=fill_color, stroke_color=stroke, stroke_size=stroke_size, place=place, calculate_only=True)
|
|
_, actual_text_box_y_size, input_text_block_x_size, actual_text_box_x_size = actual_text_box_size
|
|
if actual_text_box_y_size <= region_y_size:
|
|
line_image.write_text_box((stroke_size,0), caption, region_x_size, font_size=init_font_size, font_filename=font, color=fill_color, stroke_color=stroke, stroke_size=stroke_size, place=place)
|
|
actual_paste_x_coordinates, actual_paste_y_coordinates = coordinates
|
|
if align != "":
|
|
y_difference = region_y_size - actual_text_box_y_size
|
|
x_difference = region_x_size - actual_text_box_x_size
|
|
# Horizontal Align
|
|
if "t" in align:
|
|
#This is the default
|
|
pass
|
|
elif "b" in align:
|
|
actual_paste_y_coordinates+=y_difference
|
|
elif "cv" in align:
|
|
actual_paste_y_coordinates += int(y_difference/2)
|
|
|
|
if "l" in align:
|
|
#This is the default
|
|
pass
|
|
elif "r" in align:
|
|
|
|
actual_paste_x_coordinates+=x_difference
|
|
# elif "ch" in align:
|
|
# actual_paste_x_coordinates += int(x_difference/2)
|
|
|
|
if HIGHLIGHT_MODE:
|
|
image_draw = ImageDraw.Draw(base)
|
|
image_draw.rectangle((coordinates, (coordinates[0]+region_size[0], coordinates[1]+region_size[1])), fill="blue")
|
|
image_draw.rectangle(((actual_paste_x_coordinates, actual_paste_y_coordinates), (actual_paste_x_coordinates+actual_text_box_x_size, actual_text_box_y_size+actual_paste_y_coordinates)), fill="tan")
|
|
|
|
animated_line_image = AnimatedImage.from_image(line_image.image)
|
|
return base.paste(animated_line_image, (actual_paste_x_coordinates, actual_paste_y_coordinates))
|
|
else:
|
|
return add_text_box(base, caption, region_size, coordinates, font=font, init_font_size=init_font_size-1, align=align, color=color)
|
|
|
|
def add_text(base : Image, caption : str, region_size : 'tuple[int, int]', coordinates : 'tuple[int, int]', font : str= "arial.ttf"):
|
|
font = get_real_filename(font)
|
|
print(font)
|
|
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)
|
|
return base.paste(AnimatedImage.from_image(line_image.image), coordinates)
|
|
|
|
def add_watermark(image : Image, name_of_other_creator):
|
|
global watermark_captions
|
|
WATERMARK_HEIGHT = int(0.05 * image.height)
|
|
image_size_x, image_size_y = image.size
|
|
base = AnimatedImage.new(size=(image_size_x, image_size_y + WATERMARK_HEIGHT))
|
|
base = base.paste(image, (0,0))
|
|
|
|
marsey = get_emoji_from_rdrama("marseyjamming")
|
|
marsey = marsey.fit((WATERMARK_HEIGHT, WATERMARK_HEIGHT))
|
|
base = base.paste(marsey, (0, image_size_y))
|
|
|
|
text_line_size = int(WATERMARK_HEIGHT/2)
|
|
|
|
caption = choice(watermark_captions)
|
|
base = add_text(base, f"A meme by {name_of_other_creator} and automeme", (image_size_x, text_line_size), (WATERMARK_HEIGHT, image_size_y))
|
|
base = add_text(base, f"{caption}, 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]') -> 'AnimatedImage':
|
|
to_paste = to_paste.fit(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
|
|
|
|
return base.paste(to_paste, (x, y))
|
|
|
|
def add_border_to_image(image : Image, thickness : int = 5):
|
|
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 = AnimatedImage.new((outer_image_x_size,outer_image_y_size), color=(0,0,0))
|
|
|
|
outside_image = outside_image.paste(image, (thickness, thickness))
|
|
return outside_image
|
|
|
|
def get_emoji_from_rdrama(emoji_name) -> 'AnimatedImage':
|
|
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(get_real_filename(f"emoji_cache/{cleaned_emoji_name}.webp"))):
|
|
image = AnimatedImage.from_image(Image.open(get_real_filename(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(get_real_filename(f"emoji_cache/{cleaned_emoji_name}.webp"))
|
|
if should_flip:
|
|
image = image.flip()
|
|
return image
|
|
|
|
def get_image_file_from_url(url) -> 'AnimatedImage':
|
|
try:
|
|
r = requests.get(url)
|
|
image_file = io.BytesIO(r.content)
|
|
im = Image.open(image_file)
|
|
return AnimatedImage.from_image(im)
|
|
except:
|
|
print(f"ERROR GETTING FILE FROM {url}")
|
|
def parse_caption_file(filename):
|
|
if not exists(filename):
|
|
return []
|
|
to_return = []
|
|
with open(get_real_filename(filename), "r") as f:
|
|
for id in f.readlines():
|
|
to_return.append(id.strip())
|
|
return to_return
|
|
|
|
watermark_captions = parse_caption_file(get_real_filename(CAPTION_FILENAME))
|
|
|
|
class AnimatedImage():
|
|
def __init__(self, frames : 'list[Image.Image]'):
|
|
if len(frames) > 100:
|
|
self.frames = frames[0:100]
|
|
else:
|
|
self.frames = frames
|
|
|
|
@property
|
|
def size(self) -> 'Tuple[int, int]':
|
|
return self.frames[0].size
|
|
|
|
@property
|
|
def height(self) -> int:
|
|
return self.frames[0].size[1]
|
|
|
|
@property
|
|
def width(self) -> int:
|
|
return self.frames[0].size[0]
|
|
|
|
def flip(self) -> 'AnimatedImage':
|
|
new_frames = []
|
|
for frame in self.frames:
|
|
#frame.show()
|
|
new_frame = ImageOps.mirror(frame)
|
|
#new_frame.show()
|
|
new_frames.append(new_frame)
|
|
return AnimatedImage(new_frames)
|
|
|
|
def save(self, filename) -> None:
|
|
self.frames[0].save(f"{filename}", save_all = True, append_images = self.frames[1:], duration = 100, loop=0)
|
|
|
|
def get_binary(self) -> bytes:
|
|
output = io.BytesIO()
|
|
self.frames[0].save(output, format="webp", save_all = True, append_images = self.frames[1:], duration = 100, loop=0)
|
|
return output.getvalue()
|
|
|
|
def from_image(image: Image.Image) -> 'AnimatedImage':
|
|
frames = []
|
|
for image_frame in ImageSequence.Iterator(image):
|
|
frames.append(image_frame.copy())
|
|
return AnimatedImage(frames)
|
|
|
|
def paste(self, other : 'AnimatedImage', position : 'Tuple[int, int]') -> 'AnimatedImage':
|
|
num_self_frames = len(self.frames)
|
|
num_other_frames = len(other.frames)
|
|
|
|
new_frames = []
|
|
for i in range(get_ideal_number_of_frames(num_self_frames, num_other_frames)):
|
|
self_frame = self.frames[i%num_self_frames]
|
|
other_frame = other.frames[i%num_other_frames]
|
|
|
|
new_frame = self_frame.copy()
|
|
try:
|
|
new_frame.paste(other_frame, position, other_frame)
|
|
except ValueError:
|
|
#something about transparently idfk lol
|
|
new_frame.paste(other_frame, position)
|
|
new_frames.append(new_frame)
|
|
return AnimatedImage(new_frames)
|
|
|
|
def new(size : 'Tuple[int, int]', color=(255,255,255)) -> 'AnimatedImage':
|
|
base = Image.new(mode="RGB", size=size, color=color)
|
|
return AnimatedImage.from_image(base)
|
|
|
|
def fit(self, region : 'Tuple[int, int]') -> 'AnimatedImage':
|
|
'''
|
|
Shrinks the image while preserving it's aspect ratio, such that the new image has the same ratio but is contained in the given box.
|
|
'''
|
|
new_frames = []
|
|
for frame in self.frames:
|
|
new_frames.append(ImageOps.contain(frame, region))
|
|
return AnimatedImage(new_frames)
|
|
|
|
def stretch(self, region : 'Tuple[int, int]') -> 'AnimatedImage':
|
|
'''
|
|
Resizes the image such that the final image has the exact dimensions given by region
|
|
'''
|
|
return self.perform_transform_for_all_images(lambda image : image.resize(region, Image.ANTIALIAS))
|
|
|
|
def stretch_maintain_aspect_ratio(self, size : int) -> 'AnimatedImage':
|
|
aspect_ratio = self.height / self.width
|
|
ideal_height = int(size * aspect_ratio)
|
|
|
|
return self.stretch((size, ideal_height))
|
|
|
|
def perform_transform_for_all_images(self, transform : 'Callable[[Image.Image], Image.Image]') -> 'AnimatedImage':
|
|
new_frames = []
|
|
for frame in self.frames:
|
|
new_frames.append(transform(frame))
|
|
return AnimatedImage(new_frames)
|
|
|
|
def prime_decomposition(a):
|
|
primes = []
|
|
current = a
|
|
while True:
|
|
if current == 1:
|
|
return primes
|
|
for i in range(current+1):
|
|
if i > 1 and current % i == 0:
|
|
primes.append(i)
|
|
current = int(current/i)
|
|
break
|
|
|
|
def get_ideal_number_of_frames(frame_count_1, frame_count_2):
|
|
bigger = max(frame_count_1, frame_count_2)
|
|
smaller = min(frame_count_1, frame_count_2)
|
|
|
|
if (bigger % smaller == 0):
|
|
return bigger
|
|
else:
|
|
product = 1
|
|
for i in get_leftover_primes(bigger, smaller):
|
|
product *= i
|
|
return bigger * i
|
|
|
|
def get_leftover_primes(a_, b_):
|
|
a = max(a_, b_)
|
|
b = min(a_, b_)
|
|
a_primes = prime_decomposition(a)
|
|
b_primes = prime_decomposition(b)
|
|
|
|
leftover_primes = []
|
|
for i in b_primes:
|
|
if i in a_primes:
|
|
a_primes.remove(i)
|
|
else:
|
|
leftover_primes.append(i)
|
|
return leftover_primes
|
|
|
|
# shooting = get_emoji_from_rdrama("!marseyshooting")
|
|
# shooting2 = get_emoji_from_rdrama("marseydeterminedgun")
|
|
|
|
# base = AnimatedImage.new((500, 500))
|
|
# base = base.paste(shooting, (0,0))
|
|
# base = base.paste(shooting2.fit((50, 50)), (250,250))
|
|
# base = add_text_box(base, "killing time", (100, 500), (0, 400))
|
|
# base.save("kingshit.webp")
|
|
#create_soy_vs_chad_meme("bigsmilesoyjak", "!marseyshooting", "I have fun new toys and games for your children", "Die").save("brand_new_meme.webp")
|
|
|
|
# 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, its not fair, why do you always have to bully me around, you are such a bitch, i hate you so much, hitler was right and six million wasnt nearly enough, the jews are responsible for this and they will be punished on the day of the rope, youll be sorry then, plus i am armed and dangerous and i have a gun and i will shoot you if you keep making fun of me plus youll be in troulbe if I kill myself so how do you like that you stupid nigger bitch. fuck", words_in_background=True),
|
|
# TitleCardWebcomicPanel("One Hour Later..."),
|
|
# TwoCharacterWebcomicPanel("marseytombstone", "", "!marseycry", "He had so much to live for"),
|
|
# OneCharacterWebcomicPanel("marseylaugh", "Just Kidding!", False)
|
|
# ]).save("webcomic.webp")
|
|
create_modern_meme_from_url("https://media.giphy.com/media/gYkga3bZav66I/giphy.webp", "me when i see a black person (i am extremely racist)").save("racism.webp")
|
|
#create_classic_meme_from_url("https://rdrama.net/images/16619117705921352.webp", "I left out 'hiking' intentionally since it's outdoor hobby 101. But even if he's trying to attract a homebody some kind of interest would be good to have. Like home brewing, art, instrument, or god forbid tabletop gaming.", "This just screams most boring, generic, codependent 'man' ever. I need a wife and I'll be happy to do whatever you want to do 24/7 because I have no personality, friends, or interests").save("classic.webp")
|
|
#create_modern_meme_from_url("https://rdrama.net/images/16617243111639555.webp", "It really does, especially with that weird lighting. ANd what's up with the subset of troons with Himmler eyes?").save("modern_image.webp")
|
|
#create_classic_meme_from_emoji("marseycock", "I WANT TO PUT MY DICK", "INSIDE OF MARSEY").save("classic_with_emoji.webp")
|
|
#create_modern_meme_from_emoji("rdramajanny", "youll be sorry when i get my mop you BITCH").save("modern_emoji.webp") |