""" Common tools for drawing compatible frames in a Component's frameRender() """ from PyQt6 import QtGui from PIL import Image, ImageEnhance, ImageChops, ImageFilter from PIL.ImageQt import ImageQt from PyQt6 import QtCore import sys import os import math import logging from .. import core log = logging.getLogger("AVP.Toolkit.Frame") class FramePainter(QtGui.QPainter): """ A QPainter for a blank frame, which can be converted into a Pillow image with finalize() """ def __init__(self, width, height): image = BlankFrame(width, height) log.debug("Creating QImage from PIL image object") self.image = ImageQt(image) super().__init__(self.image) def setPen(self, penStyle): if type(penStyle) is tuple: super().setPen(QtGui.QColor(*penStyle)) else: super().setPen(penStyle) def finalize(self): log.verbose("Finalizing FramePainter") buffer = QtCore.QBuffer() buffer.open(QtCore.QBuffer.OpenModeFlag.ReadWrite) self.image.save(buffer, "PNG") import io frame = Image.open(io.BytesIO(buffer.data())) buffer.close() self.end() return frame def addShadow(frame, blurRadius, blurOffsetX, blurOffsetY): shadImg = ImageEnhance.Contrast(frame).enhance(0.0) shadImg = shadImg.filter(ImageFilter.GaussianBlur(blurRadius)) frame = shadImg.paste(frame, box=(-blurOffsetX, -blurOffsetY), mask=frame) frame = shadImg return frame def scale(scalePercent, width, height, returntype=None): width = (float(width) / 100.0) * float(scalePercent) height = (float(height) / 100.0) * float(scalePercent) if returntype == str: return (str(math.ceil(width)), str(math.ceil(height))) elif returntype == int: return (math.ceil(width), math.ceil(height)) else: return (width, height) def defaultSize(framefunc): """Makes width/height arguments optional""" def decorator(*args): if len(args) < 2: newArgs = list(args) if len(args) == 0 or len(args) == 1: height = int(core.Core.settings.value("outputHeight")) newArgs.append(height) if len(args) == 0: width = int(core.Core.settings.value("outputWidth")) newArgs.insert(0, width) args = tuple(newArgs) return framefunc(*args) return decorator def FloodFrame(width, height, RgbaTuple): return Image.new("RGBA", (width, height), RgbaTuple) @defaultSize def BlankFrame(width, height): """The base frame used by each component to start drawing.""" return FloodFrame(width, height, (0, 0, 0, 0)) @defaultSize def Checkerboard(width, height): """ A checkerboard to represent transparency to the user. """ # TODO: Would be cool to generate this image with numpy instead. log.debug("Creating new %s*%s checkerboard" % (width, height)) image = FloodFrame(1920, 1080, (0, 0, 0, 0)) image.paste(Image.open(os.path.join(core.Core.wd, "gui", "background.png")), (0, 0)) image = image.resize((width, height)) return image