def generate_panel( panel: dict, colors: dict, session: Optional[requests.Session] = requests.Session() ) -> Image.Image: image = Image.new('RGB', get_size(panel)) canvas = ImageDraw.Draw(image) image2 = Image.new('RGBA', (image.width * 2, image.height * 2)) canvas2 = ImageDraw.Draw(image2) size = get_size(panel) background = ImageUtil.ratio_resize( ImageUtil.get_image(panel['displayAssets'][0]['background'], session).convert('RGBA'), *size) image.paste(background, ImageUtil.center_x(background.width, image.width, 0), background) display_asset = ImageUtil.ratio_resize( ImageUtil.get_image(panel['displayAssets'][0]['url'], session).convert('RGBA'), *size) image.paste(display_asset, ImageUtil.center_x(display_asset.width, image.width, 0), display_asset) canvas.polygon( ((0, size[1] - PRICE_HEIGHT), (size[0], size[1] - PRICE_HEIGHT), (size[0], size[1]), (0, size[1])), fill=(14, 14, 14)) vbucks = ImageUtil.ratio_resize( ImageUtil.open('vbucks.png').point(lambda x: x * 0.8).convert( 'RGBA').rotate(-15), 40, 40) pos = size[0] - vbucks.width - 5 image.paste(vbucks, (pos, size[1] - vbucks.height + 10), vbucks) text = f"{panel['price']['finalPrice']:,}" fonts = name_fonts.fonts_size(15, 15, 15) x, y = fonts.text_size(text) pos = pos - x - 3 fonts.write_text(canvas, text, (pos, size[1] - y - 4), fill=(160, 175, 185)) if panel['price']['finalPrice'] != panel['price']['regularPrice']: text = f"{panel['price']['regularPrice']:,}" fonts = name_fonts.fonts_size(15, 15, 15) x, y = fonts.text_size(text) pos = pos - x - 6 fonts.write_text(canvas, text, (pos, size[1] - y - 4), fill=(100, 100, 100)) canvas2.line((((pos - 2) * 2, (size[1] - y - 4 + 10) * 2), ((pos + x + 3) * 2, (size[1] - y - 4 + 6) * 2)), fill=(100, 110, 110), width=3 * 2) canvas2.polygon( ((0, (size[1] - PRICE_HEIGHT - NAME_HEIGHT - RARITY_HEIGHT) * 2), (size[0] * 2, (size[1] - PRICE_HEIGHT - NAME_HEIGHT - RARITY_HEIGHT - SLOPE) * 2), (size[0] * 2, (size[1] - PRICE_HEIGHT - NAME_HEIGHT - SLOPE) * 2), (0, (size[1] - PRICE_HEIGHT - NAME_HEIGHT) * 2)), fill=colors[panel['series']['id'] if panel['series'] is not None else panel['rarity']['id']]) canvas2.polygon( ((0, (size[1] - PRICE_HEIGHT - NAME_HEIGHT) * 2), (size[0] * 2, (size[1] - PRICE_HEIGHT - NAME_HEIGHT - SLOPE) * 2), (size[0] * 2, (size[1] - PRICE_HEIGHT) * 2), (0, (size[1] - PRICE_HEIGHT) * 2)), fill=(30, 30, 30)) image2.thumbnail(image.size, Image.LANCZOS) image.paste(image2, (0, 0), image2) fonts = name_fonts.fonts_size(20, 20, 20) x, y = fonts.text_size(panel['displayName']) fonts.write_text(canvas, panel['displayName'], ImageUtil.center_x(x, image.width, size[1] - PRICE_HEIGHT - y - 10), fill=(255, 255, 255)) icons = [ ImageUtil.ratio_resize( ImageUtil.open(filename).convert('RGBA'), 30, 30) for filename in set( itertools.chain(*[ get_user_facing_flag_images(item) for item in panel['granted'] ])) ] x = size[0] - 10 for icon in icons: x -= icon.width image.paste(icon, (x, size[1] - PRICE_HEIGHT - NAME_HEIGHT - RARITY_HEIGHT - 15 - icon.height), icon) x -= 10 return image
def generate_section( section: dict, colors: dict, now: datetime.datetime, session: Optional[requests.Session] = requests.Session() ) -> Image.Image: if len(section['panels'] ) == 1 and section['panels'][0]['tileSize'] == 'Small': image = Image.new('RGBA', (MARGIN_LEFT + get_section_width(section) + MARGIN_RIGHT, Y_MARGIN + SMALL_SIZE[1])) else: image = Image.new('RGBA', (MARGIN_LEFT + get_section_width(section) + MARGIN_RIGHT, Y_MARGIN + NORMAL_SIZE[1])) canvas = ImageDraw.Draw(image) x = MARGIN_LEFT size = 50 if section['name']: fonts = name_fonts.fonts_size(size, size, size) x = 50 final_x, _ = fonts.write_text(canvas, section['name'].upper(), (x, Y_MARGIN // 2 - 25)) x += final_x if section['until'] is not None: timer = ImageUtil.ratio_resize( ImageUtil.open('shop_timer.png').convert('RGBA'), size, size) x += 12 image.paste(timer, (x, Y_MARGIN // 2 - 15 - 4), timer) end = section['until'] - now m, s = divmod(end.seconds, 60) h, m = divmod(m, 60) timer_text = ("{}:{:0>2}:{:0>2}".format(h, m, s) if end > datetime.timedelta(hours=1) else "{}:{:0>2}".format(m, s)) x += timer.width + 6 fonts = name_fonts.fonts_size(size // 2, size // 2, size // 2) _, y = fonts.text_size(timer_text) fonts.write_text(canvas, timer_text, (x, Y_MARGIN // 2 - 15 + y // 2), fill=(115, 200, 235)) with ThreadPoolExecutor() as executor: futures = [ executor.submit(generate_panel, panel, colors, session) for panel in section['panels'] ] x = MARGIN_LEFT small_count = 0 for num, future in enumerate(futures): try: panel_image = future.result() except Exception: print('Failed to generate panel', file=sys.stderr) traceback.print_exc() else: panel = section['panels'][num] size = get_size(panel) if panel['tileSize'] == 'Small': small_count += 1 if small_count % 2 == 1: pos = (x, Y_MARGIN) x += size[0] + X_MARGIN else: pos = (x - size[0] - X_MARGIN, Y_MARGIN + (NORMAL_SIZE[1] - size[1] * 2) + size[1]) else: pos = (x, Y_MARGIN) x += size[0] + X_MARGIN image.paste(panel_image, pos) if panel['banner'] is not None: font_size, minus = name_fonts.fit_fonts_size( image.width - 25, 16, panel['banner']['name']) fonts = name_fonts.fonts_size(font_size, font_size, font_size) text_width = fonts.text_size(panel['banner']['name'])[0] banner_height = 32 color = 'red' if panel['banner'][ 'intensity'] == 'Low' else 'yellow' banner_rear = ImageUtil.ratio_resize( ImageUtil.open(f'{color}_banner_rear.png').convert('RGBA'), 0, banner_height, resample=Image.BICUBIC) banner_middle = ImageUtil.open( f'{color}_banner_middle.png').convert('RGBA').resize( (text_width, banner_height)) banner_front = ImageUtil.ratio_resize(ImageUtil.open( f'{color}_banner_front.png').convert('RGBA'), 0, banner_height, resample=Image.BICUBIC) image.paste(banner_rear, (pos[0] - 15, pos[1] - 15), banner_rear) image.paste(banner_middle, (pos[0] - 15 + banner_rear.width, pos[1] - 15), banner_middle) image.paste(banner_front, (pos[0] - 15 + banner_rear.width + banner_middle.width, pos[1] - 15), banner_front) fonts.write_text( canvas, panel['banner']['name'], (pos[0] - 15 + banner_rear.width, pos[1] - 15 + 5), fill=(255, 255, 255) if panel['banner']['intensity'] == 'Low' else (0, 0, 0)) return image