aboutsummaryrefslogtreecommitdiff
path: root/src/avp/toolkit/frame.py
blob: 829b05b78978db8de23b2be4cfb5e90ffda6bc22 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
"""
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